Почему классы имеют тенденцию быть определенными как интерфейс в наше время?

Эти 2-3 прошлых года, много проектов, которые я вижу, как открытый исходный код Cuyahoga C# CMS, имеют тенденцию определять персистентный и не персистентные классы как Interface. Почему? Существует ли серьезное основание? TDD? Насмешка? Шаблон разработки?...

13
задан Afshar Mohebbi 19 July 2010 в 13:44
поделиться

5 ответов

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

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

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

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

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

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

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

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

calculateSalary (... IBenefitCalculator bc, ...)

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

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

21
ответ дан 1 December 2019 в 21:23
поделиться

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

1
ответ дан 1 December 2019 в 21:23
поделиться

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

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

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

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, и ваш класс «просто обработает это».

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

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

3
ответ дан 1 December 2019 в 21:23
поделиться

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

1
ответ дан 1 December 2019 в 21:23
поделиться

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

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();
}

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

0
ответ дан 1 December 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: