Когда класс должен реализовывать интерфейс, а когда нет? - PullRequest
0 голосов
/ 08 сентября 2010

Должен ли класс всегда реализовывать интерфейс для обеспечения своего рода "контракта" в классе?

Когда класс не должен реализовывать интерфейс?

Редактировать: Имеется в виду, когда стоит, чтобы класс реализовывал интерфейс? Почему бы не сделать так, чтобы у класса были только открытые члены и закрытые члены с различными функциями доступа / установки?

(Примечание: речь не идет о COM)

Ответы [ 7 ]

16 голосов
/ 08 сентября 2010

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

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

Многие классы в .NET Framework не реализуют никаких интерфейсов.

4 голосов
/ 08 сентября 2010

Используйте интерфейс только тогда, когда это необходимо.
То есть: когда вы хотите иметь разные реализации для определенной абстракции.

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

2 голосов
/ 08 сентября 2010

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

Рассмотрим, например, вычисление некоторой произвольной сложности. Это вычисление используется 6 различными классами, и поэтому, чтобы избежать дублирования кода, вычисление заключено в его собственный класс, Калькулятор. Каждый из 6 классов содержит ссылку на калькулятор. Теперь скажите, что ваш клиент приходит к вам и говорит, что при одном использовании Калькулятора, если выполняются определенные условия, вместо этого следует использовать другой расчет. У вас может возникнуть желание просто поместить эти два правила (правило использования и бизнес-правило) и новый алгоритм вычислений в класс Calculator, но если вы это сделаете, то произойдут две вещи; Во-первых, вы делаете Калькулятор осведомленным о некоторых деталях реализации (как они используются) вне его области действия, которые ему не нужно знать и которые могут измениться позже. Во-вторых, остальные 5 классов, использующих Calculator, которые работали без перебоев, должны быть перекомпилированы, поскольку они ссылаются на измененный класс, и должны быть протестированы, чтобы убедиться, что вы не нарушили их функциональность, изменив для 6 класса.

«Правильным» решением для этого является интерфейс. Определив интерфейс ICalculator, который предоставляет методы, вызываемые другими классами, вы нарушаете конкретную зависимость 6 классов от конкретного класса Calculator. Теперь каждый из 6 классов может иметь ссылку на ICalculator. В 5 из этих классов вы предоставляете тот же класс Calculator, который у них был всегда, и отлично работаете с ним. 6-го числа вы предоставляете специальный калькулятор, который знает дополнительные правила. Если бы вы сделали это с самого начала, вам не пришлось бы трогать другие 5 классов, чтобы внести изменения в 6-й.

Суть в том, что классы не должны знать точную природу других объектов, от которых они зависят; вместо этого им нужно только знать, что этот объект для них сделает. Абстрагируя то, что объект ДЕЛАЕТ от того, чем объект является, несколько объектов могут делать похожие вещи, и классы, которым требуются эти вещи, не должны знать разницу.

Слабая связь, наряду с «высокой связностью» (объекты обычно должны быть специалистами, которые знают, как выполнять небольшой, очень тесно связанный набор задач), является основой для большинства шаблонов проектирования программного обеспечения, которые вы увидите как вы прогрессируете в теории разработки программного обеспечения.

В отличие от пары ответов, существуют методологии проектирования (например, SOLID), которые утверждают, что вы ВСЕГДА должны устанавливать зависимости как абстракции, такие как абстрактный базовый класс или интерфейс, и НИКОГДА не иметь один класс, зависящий от другого конкретного класса.,Логика здесь заключается в том, что при разработке коммерческого программного обеспечения начальный набор требований для приложения очень мал, но это безопасное предположение, если не гарантия, что набор требований будет расти и изменяться.Когда это происходит, программное обеспечение должно расти.Создание даже небольших приложений в соответствии со строгими принципами проектирования позволяет расширять программное обеспечение, не вызывая проблем, которые являются естественным следствием плохого проектирования (большие классы с большим количеством кода, изменения одного класса, влияющие на другие непредсказуемым образом и т. Д.).Однако искусство разработки программного обеспечения и ограничения по времени и деньгам таковы, что вы можете (и должны) быть умными и говорить: «Из того, что я знаю о том, как эта система будет развиваться, это область, которая нуждается вбыть хорошо разработанным, чтобы позволить адаптацию, в то время как этот другой раздел почти наверняка никогда не изменится ".Если эти предположения изменятся, вы можете вернуться и реструктурировать области кода, которые вы разработали, очень просто, чтобы быть более надежными, прежде чем расширять эту область.Но вы должны быть готовы и иметь возможность вернуться и изменить код после его первой реализации.

2 голосов
/ 08 сентября 2010

Интерфейсы становятся все более необходимыми, когда вы проводите модульное тестирование, но все зависит от контекста вашей разработки. Как сказал Марк, интерфейс - это контракт, и его реализация заставляет вас придерживаться «правил» этого контракта.

Если вы пытаетесь обеспечить реализацию определенных методов, то для этого идеально подойдет интерфейс.

Вот несколько хороших примеров:

1 голос
/ 08 сентября 2010

Класс не должен реализовывать интерфейс / интерфейсы, если только вы не хотите сказать другим частям вашей программы - "Этот класс может делать эти вещи (но не указывать, как именно он делает то, что делает)".
Когда вы захотите это сделать?
Например, скажем, у вас есть игра, в которой у вас есть животные ... И скажите, что когда животное видит человека, оно издает звук (будь то кора, рев и т. Д.).).
Если все животные будут использовать интерфейс IMakeSound, в котором есть метод MakeSound, вам не нужно заботиться о том, какое животное должно издавать этот звук. Все, что вам нужно сделать, этоиспользуйте часть «IMakeSound» животного и вызовите его метод.
Я должен добавить, что когда кто-то читает в объявлении класса, что он реализует определенный интерфейс, он много ему говорит об этом классе, что является еще одним преимуществом.

1 голос
/ 08 сентября 2010

Возможно, вы не всегда хотите интерфейс. Учтите, что вы можете выполнять аналогичные задачи с делегатом. В Java я использовал Runnable Interface для многопоточных приложений. Теперь, когда я программирую на .NET, я очень полагаюсь на делегатов для выполнения своих многопоточных приложений. Эта статья помогает объяснить необходимость делегата против интерфейса.

Когда использовать делегаты вместо интерфейсов (Руководство по программированию в C #)

Делегаты обеспечивают немного большую гибкость, поскольку в Java я обнаружил, что любая задача, которую я выполнял в C с помощью указателя функции, теперь требует включения в интерфейс.

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

Если вы хотите лучше понять интерфейсы, я предлагаю вам прочитать «Шаблоны проектирования в первую очередь» .

1 голос
/ 08 сентября 2010

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

абстрактный класс Animal
класс Dog расширяет Animal

В этом случае интерфейс Animal == (или контракт) для Dog

интерфейс Измеримый
класс Кубок реализует Измеримый

В этом случае Измеримый == Интерфейс для Кубка

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