Как да не използвам интерфейси в C #

Когато проектирате приложение, често ще трябва да използвате интерфейси и абстрактни класове. Тази статия разглежда някои често срещани примери за „злоупотреба с интерфейса“ и стратегиите, които можем да използваме, за да ги избегнем. Той също така обсъжда какво се разбира под принципа, „програма към интерфейс, а не към изпълнение“.

Какво представляват интерфейсите?

Първо, нека да разберем интерфейсите и защо те са необходими при програмирането. Интерфейсът е строго договор; няма никакво изпълнение. Интерфейсът съдържа само декларации за членове. Можете да имате декларации на методи, но не и дефиниции. Членовете, декларирани в интерфейс, трябва да бъдат внедрени във типовете (класове и структури), които разширяват или изпълняват интерфейса. Интерфейсът не може да съдържа полета. Интерфейсът не може да бъде сериализиран, тъй като не може да има членове на данни. Както казах, интерфейсът може да има само декларации, но не и дефиниции.  

Избягвайте да правите промени в интерфейсите 

Клас или структура, която разширява интерфейс, трябва да реализира всички свои членове. Ако внедряването се промени, вашият код ще продължи да работи. Ако обаче договорът, т.е. интерфейсът, се промени, ще трябва да промените изпълнението на всички типове, които разширяват интерфейса. С други думи, всяка промяна в интерфейса ще повлияе на всички типове, които разширяват интерфейса. Типовете, разширяващи интерфейса, трябва да се придържат към договора. Така че, използвайте интерфейси само когато рядко се налага да ги променяте. Освен това обикновено е по-добре да създадете нов интерфейс, отколкото да промените съществуващ. 

Програмирайте към интерфейс, а не към изпълнение

Може би сте чували думите „програма към интерфейс, а не към изпълнение“ от време на време. Може да сте използвали интерфейси в кода си, но все още сте програмирали изпълнението. Нека сега разгледаме разликата между двата подхода.

Когато програмирате към интерфейс, вие използвате най-общата абстракция (интерфейс или абстрактен клас) вместо конкретна реализация. Тъй като интерфейсите гарантират еднаквост, програмирането на интерфейс предполага, че можете да обработвате подобни обекти по еднакъв начин. По този начин вие сте отделени от изпълнението - т.е. вашите внедрения могат да варират. Това добавя гъвкавост и към вашите дизайни.

Следният кодов фрагмент илюстрира програмиране на интерфейс. Помислете за интерфейс, наречен IRepository, който съдържа декларацията на няколко метода. Класовете ProductRepository и CustomerRepository разширяват интерфейса IRepository и прилагат методите, декларирани в интерфейса IRepository, както е показано по-долу.

публичен интерфейс IRepository

    {

        // Някакъв код

    }

    публичен клас ProductRepository: IRepository

    {

        // Някакъв код

    }

    публичен клас CustomerRepository: IRepository

    {

        // Някакъв код

    }

Следният код може да се използва за създаване на екземпляр на ProductRepository.

Хранилище на IRepository = ново ProductRepository ();

Идеята е, че тук можете да използвате всеки клас, който реализира интерфейса IRepository. Така че, следващото твърдение също е валидно.

Хранилище на IRepository = ново клиентско хранилище ();

Когато програмирате за изпълнение, тази еднаквост се губи. Вместо това обикновено ще имате някои конструкции, като например „if..else“ или „switch..case“, за контрол на поведението във вашия код.

Избягвайте прекалената употреба на интерфейси

Свързването на всеки клас с интерфейс не е добра практика. Прекалената употреба на интерфейси по този начин създава ненужна сложност, въвежда излишък на код, нарушава YAGNI и намалява четливостта и поддръжката на кодовата основа. Интерфейсите се използват за групиране на обекти, които имат идентично поведение. Ако обектите нямат идентично поведение, няма нужда от това групиране. Използването на интерфейси, когато няма да имате множество реализации, е пример за прекалена употреба на интерфейса.

Създаването на интерфейс за клас, който съответства на публичните членове на класа, е доста често срещано. По този начин изобщо не добавяте никаква стойност - просто дублирате интерфейса на класа, без да добавяте истинска абстракция.

Нека сега разгледаме пример за това как се прекалява с интерфейсите. Помислете за следния интерфейс, наречен IProduct.

публичен интерфейс

    {

        int Id {get; комплект; }

        низ ProductName {get; комплект; }

        двойна цена {get; комплект; }

        int Количество {get; комплект; }

    }

Класът Product разширява интерфейса на IProduct, както е показано по-долу.

публичен клас Продукт: IProduct

    {

        public int Id {get; комплект; }

        публичен низ ProductName {get; комплект; }

        публична двойна цена {get; комплект; }

        public int Количество {get; комплект; }

    }

Ясно е, че не се нуждаем от IProduct интерфейс, тъй като интерфейсът и неговото изпълнение са идентични. Излишният код е ненужен.

Нека разгледаме друг пример. Следният кодов фрагмент показва интерфейс, наречен IProductManager, имащ декларацията за два метода, а именно Запазване и актуализиране.

 публичен интерфейс IProductManager

    {

        void Запазване (продукт на IProduct);

        невалидна актуализация (продукт на IProduct);

    }

Интерфейсът IProductManager съдържа декларациите на публичните методи на класа ProductManager. Ето как изглежда класът ProductManager.

 публичен клас ProductManager: IProductManager

    {

        публична невалидност Запазване (продукт от IPпродукт)

        {

           // Напишете вашата реализация тук

        }

        публична невалидна актуализация (продукт на IProduct)

        {

            // Напишете вашата реализация тук

        }

    }

Интерфейсите IProduct и IProductManager са примери за прекалена употреба на интерфейса. И двата интерфейса имат една реализация и изобщо не добавят никаква стойност.

Използвайки интерфейси, можете да премахнете ненужните съединители в кода си и да направите кода лесно проверяваем. Трябва обаче да се избягва прекомерната употреба на интерфейси. Използвайте интерфейси само когато има повече от едно изпълнение от тях. Можете също да използвате интерфейси, когато имате клас, който има много роли или който има множество отговорности. В този случай вашият клас може да реализира множество интерфейси - по един за всяка роля.