Почему в наши дни классы определяются как интерфейс? - PullRequest
13 голосов
/ 19 июля 2010

В эти 2-3 последних года многие проекты, которые я вижу, такие как C # CMS с открытым исходным кодом Cuyahoga, обычно определяют постоянные и непостоянные классы как Interface.Зачем?Есть ли веская причина?TDD?Дразнящий?Шаблон дизайна?...

Ответы [ 5 ]

21 голосов
/ 19 июля 2010

Основная причина в том, что это облегчает такие методы, как внедрение зависимостей . Это, в свою очередь, обеспечивает большую гибкость программного обеспечения и упрощает повторное использование и рекомбинацию существующего кода. Примеры того, где это полезно, включают различные формы модульного тестирования (как вы упомянули), а также большинство других форм «регулярного» повторного использования кода.

Простой пример:

Скажем, у вас есть метод расчета зарплаты emplyoee. В качестве части своей подписи он принимает объект, который вычисляет их преимущества, например экземпляр BenefitCalculator:

calculateSalary(... BenefitCalculator bc, ...)

Изначально ваш дизайн имеет только один класс BenefitCalculator. Но позже выясняется, что вам нужно более одного класса, например, потому что разные части программного обеспечения должны использовать разные алгоритмы (возможно, для поддержки разных стран, или потому что алгоритм должен настраиваться пользователем ...). В этом случае вместо того, чтобы раздуть существующую реализацию BenefitCalculator, имеет смысл создать новый класс (ы), например, BenefitCalculatorFrance или BenefitCalculatorSimple и т. Д.

Теперь, если вы используете подпись

calculateSalary(... BenefitCalculator bc, ...)

, вы как бы облажались, потому что вы не можете предоставить разные реализации. Однако, если вы используете

Рассчитать зарплату (... IBenefitCalculator до н.э., ...)

вы можете просто сделать так, чтобы все классы реализовывали интерфейс.

Это на самом деле просто особый случай "слабой связи": требовать как можно меньше от других частей кода. В этом случае не требуйте определенного класса; вместо этого просто требуйте, чтобы определенные методы существовали, что и делает интерфейс.

3 голосов
/ 19 июля 2010

Прежде всего, вы не можете определить класс как интерфейс.Ваш класс реализует интерфейс.

Интерфейсы используются как один из способов включить полиморфное поведение.Каждый класс, который реализует интерфейс, может указывать свою собственную реализацию методов, определенных в интерфейсе.Возьмем, к примеру, следующее:

Вы пишете банковское программное обеспечение.Ваша задача - написать обработчик транзакций.Теперь вы знаете, что вам нужно обрабатывать различные виды транзакций (депозиты, снятие средств, переводы).Вы можете написать код, который выглядит следующим образом:

public class TransactionProcessor
{
    public void ProcessDeposit( // Process Deposit );
    public void ProcessWithdraw( // Process Withdraw );
    public void ProcessTransfer( // Process Transfer );
}

И затем каждый раз, когда кто-то добавляет новый тип транзакции, вы должны изменить свой класс.Или вы можете:

public interface ITransaction { void Process(); }

public class TransactionProcessor
{
    public void ProccessTransaction(ITransaction t) { t.Process(); }
}

Теперь вам не нужно изменять свой код для обработки нового типа транзакции.Вам просто нужно, чтобы люди создали свой собственный класс, который реализует ITransaction, и ваш класс «просто справится с этим».

Это позволяет вам менять реализации интерфейса в зависимости от ваших потребностей.Он также позволяет использовать такие вещи, как Dependency Injection и Mocking Frameworks для модульного тестирования.

В целом это действительно просто еще один способ сделать ваш код более гибким.

1 голос
/ 20 июля 2010

В последние годы IoC-контейнеры стали очень популярны среди разработчиков.Например, Unity Container от Microsoft Practices.Итак, в начале вашего приложения вы можете зарегистрировать конкретные классы, которые реализуют интерфейсы, а затем, например, все классы, которые содержат эти интерфейсы в своих конструкторах, или их свойства, отмеченные атрибутом [Dependency], будут заполнены при создании экземпляров объектов черезРешения Unity контейнера.Это очень полезно в приложениях со сложными зависимостями, когда один интерфейс может быть реализован в трех разных классах.И все это невозможно без использования интерфейсов.

1 голос
/ 19 июля 2010

Преимущество интерфейсов заключается в том, что они делают вас независимыми от реализации, и это хорошо.

0 голосов
/ 29 июля 2010

На очень скучном уровне интерфейсы также могут помочь ускорить компиляцию.

public class A {
   B b;
}

public class B {
   public int getCount() {
       return 10;
   }
}

В этом случае каждый раз, когда вносятся внутренние изменения в B , компилятору необходимо-оценить A , чтобы определить, нужно ли его перекомпилировать.

Вместо этого мы используем интерфейсы:

class A {
   IB b;
}

interface IB {
   int getCount();
}

class B : IB {
   public int getCount() {
       return 10;
   }
}

В этом случае A зависит толькона IB .Никаких изменений в B не требуется учитывать A во время компиляции.

В масштабе это влияние на оценку зависимостей короткого замыкания может значительно ускорить компиляцию больших баз кода.Это особенно полезно, когда существует много классов, зависящих от одного, который сильно изменяется.

Очевидно, что это преимущество времени компиляции работает, только если классы не имеют статической зависимости от классов реализации.Выполнение следующих действий полностью исключило бы эту выгоду:

class A {
    IB b = new B();
}

Вот тут и вводится Dependency Injection. Контейнер DI создаст B и предоставит его A как IB , поэтому A не должен иметь статическую зависимость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...