2010-09-07 3 views
0

Должен ли класс реализовать интерфейс всегда, чтобы обеспечить какой-то «контракт» в классе?Когда класс должен реализовывать интерфейс, а когда нет?

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

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

(Примечание: Не говорить о COM)

+2

Что такое «интерфейс класса»? Классы и интерфейсы - это разные вещи на C#. – Oded

+0

@Oded: Моя формулировка может быть слегка отключена, но я имею в виду, что интерфейсы привязаны к классу, и я не знал, как правильно это обозначать. – Signal101

+2

Что значит мой «интерфейс класса». В классах C# и интерфейсах разные, но связанные вещи. –

ответ

16

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

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

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

+2

Основная причина, по которой вы, возможно, захотите иметь интерфейс для класса, не планируя использовать этот интерфейс для других конкретных классов, - это модульное тестирование. Это часто полезно, чтобы помочь изолировать зависимости и интерфейсы заглушки/макета для введения стимулов и/или чувства поведения. –

4

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

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

+1

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

1

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

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

В этом случае животных == Интерфейс (или контракт) Собака

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

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

+0

Хорошая точка. Interace в C++ - это всего лишь абстрактный класс. – 2010-09-07 20:18:16

2

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

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

Есть некоторые хорошие примеры здесь:

1

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

When to Use Delegates Instead of Interfaces (C# Programming Guide)

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

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

Если вы хотите лучше понять интерфейсы, я предлагаю вам прочитать "Head First Design Patterns".

1

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

2

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

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

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

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

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

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