Я редко использую интерфейсы и нахожу их общими в чужом коде.
Также я создаю подклассы и суперклассы (создавая свои собственные классы) редко в своем коде.
- Это плохо?
- Вы бы предложили изменить этот стиль?
- У этого стиля есть побочные эффекты?
- Это потому, что я не работал над крупными проектами?
programming-practices
coding-style
interfaces
Джиниш Джозеф
источник
источник
Ответы:
Есть несколько причин, почему вы можете захотеть использовать интерфейсы:
http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx
Интерфейсы, как и все остальное в программировании. Если они вам не нужны, не используйте их. Я видел, что они широко используются как вопрос стиля, но если вам не нужны особые свойства и возможности, которые предоставляет интерфейс, я не вижу преимущества их использования «просто потому, что».
источник
И наследование классов, и интерфейсы имеют свое место. Наследование означает «является», в то время как интерфейс предоставляет контракт, который определяет, что «ведет себя как».
Я бы сказал, что использование интерфейсов чаще всего не является плохой практикой. В настоящее время я читаю «Эффективные C # - 50 конкретных способов улучшить ваш C #» Билла Вагнера. Пункт номер 22 гласит, и я цитирую, «Предпочитаю определение и реализацию интерфейсов наследования».
Обычно я использую базовые классы, когда мне нужно определить конкретную реализацию общего поведения между концептуально связанными типами. Чаще всего я использую интерфейсы. Фактически, я обычно начинаю с определения интерфейса для класса, когда начинаю его создавать ... даже если в конце концов я не скомпилирую интерфейс, я обнаружу, что он помогает начать с определения общедоступного API интерфейса. класс с самого начала. Если я обнаружу, что у меня есть несколько классов, реализующих интерфейс, и логика реализации идентична, только тогда я задам себе вопрос, имеет ли смысл реализовывать общий базовый класс между типами.
Пару цитат из книги Билла Вагнера ...
все производные классы немедленно включают это поведение. Добавление члена к интерфейсу нарушает все классы, которые реализуют этот интерфейс. Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Они не будут содержать новый метод и больше не будут компилироваться. Каждый разработчик должен обновить этот тип, чтобы включить нового члена. Выбор между абстрактным базовым классом и интерфейсом - это вопрос о том, как лучше поддерживать абстракции с течением времени. Интерфейсы фиксированы: интерфейс освобождается как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ". Вы освобождаете интерфейс как контракт для набора функций, которые может реализовать любой тип. Базовые классы могут быть расширены с течением времени. Эти расширения становятся частью каждого производного класса. Две модели могут быть смешаны для повторного использования кода реализации при поддержке нескольких интерфейсов ".
«Интерфейсы кодирования обеспечивают большую гибкость для других разработчиков, чем кодирование для базовых типов классов».
«Использование интерфейсов для определения API для класса обеспечивает большую гибкость».
«Когда ваш тип предоставляет свойства как типы классов, он предоставляет весь интерфейс этому классу. Используя интерфейсы, вы можете выбрать отображение только тех методов и свойств, которые вы хотите использовать для клиентов».
«Базовые классы описывают и реализуют общие поведения для связанных конкретных типов. Интерфейсы описывают элементарные части функциональности, которые могут реализовывать несвязанные конкретные типы. Оба имеют свое место. Классы определяют типы, которые вы создаете. Интерфейсы описывают поведение этих типов как части функциональности. Если вы поймете различия, вы создадите более выразительные проекты, которые будут более устойчивыми перед лицом изменений. Используйте иерархии классов для определения связанных типов. Предоставьте функциональность, используя интерфейсы, реализованные в этих типах ».
источник
Единственное, что не было упомянуто, это тестирование: во всех библиотеках C # mocking, а также в некоторых Java, классы не могут быть смоделированы, если они не реализуют интерфейс. Это приводит к тому, что многие проекты, следуя методам Agile / TDD, дают каждому классу собственный интерфейс.
Некоторые люди считают эту лучшую практику, потому что она «уменьшает сцепление», но я не согласен - я думаю, что это всего лишь обходной путь к недостатку в языке.
Я думаю, что интерфейсы лучше всего использовать, когда у вас есть два или более классов, которые, абстрактно, делают «одно и то же», но по-разному.
Например, .Net Framework имеет несколько классов, которые хранят списки вещей , но все они хранят эти вещи по-разному. Таким образом, имеет смысл иметь абстрактный
IList<T>
интерфейс, который можно реализовать разными способами.Вы также должны использовать его, если вы хотите, чтобы 2+ класса были взаимозаменяемыми или заменяемыми в будущем. Если в будущем появится новый способ хранения списков
AwesomeList<T>
, то при условии, что вы использовали егоIList<T>
в своем коде, его изменениеAwesomeList<T>
будет означать лишь изменение нескольких десятков строк, а не нескольких сотен / тысяч.источник
Основным результатом неиспользования наследования и интерфейсов, когда они уместны, является тесная связь . Это может быть немного трудно научиться распознавать, но обычно наиболее очевидным симптомом является то, что когда вы вносите изменения, вы обнаруживаете, что вам часто приходится просматривать целую кучу других файлов, чтобы внести изменения в волновой эффект.
источник
Нет, это совсем не плохо. Явные интерфейсы должны использоваться, если и только если два класса с общим интерфейсом должны быть взаимозаменяемыми во время выполнения. Если они не должны быть взаимозаменяемыми, не наследуйте их. Это так просто. Наследование хрупкое, и его следует избегать везде, где это возможно. Если вы можете избежать этого или использовать дженерики вместо этого, тогда делайте.
Проблема в том, что в таких языках, как C # и Java со слабыми обобщениями времени компиляции, вы можете в конечном итоге нарушить DRY, потому что нет способа написать один метод, который может иметь дело с более чем одним классом, если все классы не наследуются от одной и той же базы. C # 4
dynamic
может справиться с этим, хотя.Дело в том, что наследование похоже на глобальные переменные - как только вы добавите его, и ваш код зависит от него, Бог поможет вам убрать его. Тем не менее, вы можете добавить его в любое время и даже добавить без изменения базового класса, используя своего рода оболочку.
источник
Да, не (или слишком редко) использование интерфейсов, вероятно, плохо. Интерфейсы (в абстрактном смысле, а языковая конструкция C # / Java является довольно хорошим приближением этого абстрактного смысла) определяют явные точки взаимодействия между системами и подсистемами. Это помогает уменьшить сцепление и делает систему более удобной в обслуживании. Как и все, что улучшает ремонтопригодность, оно становится тем важнее, чем больше система.
источник
Я не использовал интерфейс в течение многих лет. Конечно, это потому, что я программирую почти исключительно на Erlang уже много лет, а сама концепция интерфейса просто не существует. (Самое близкое, что вы получите, - это «поведение», и это не совсем то же самое, если вы не сильно прищуриваетесь и не смотрите на них краем глаза.)
Так что, действительно, ваш вопрос зависит от парадигмы (в данном случае ООП) и, кроме того, действительно очень зависит от языка (есть языки ООП без интерфейсов).
источник
Если вы говорите об использовании Java, одной из причин использования интерфейсов является то, что они позволяют использовать прокси-объекты без библиотек генерации кода. Это может быть существенным преимуществом, когда вы работаете со сложной средой, такой как Spring. Более того, для некоторых функций требуются интерфейсы: RMI является классическим примером этого, поскольку вы должны описать предоставляемые вами функциональные возможности в терминах интерфейсов (которые наследуются
java.rmi.Remote
), как бы вы их ни реализовывали.источник
Вещи меняются ( MS Moles ), но главная причина, по которой я думаю, что кодировать почти исключительно для интерфейсов - это то, что их легко смоделировать и естественным образом вписать в архитектуру IoC.
IMO, вы должны когда-либо работать только с интерфейсами или полностью тупыми DAO, где это возможно. Как только вы попадаете в такое мышление и начинаете использовать библиотеку, которая не раскрывает себя через интерфейсы и делает все через конкретные объекты, я должен быть честным, все это кажется немного неуклюжим.
источник
В старомодных проектах используются интерфейсы, чтобы избежать циклических ссылок, потому что циклические ссылки могут стать огромной проблемой обслуживания в долгосрочной перспективе.
Плохо:
Неплохо:
Обычно A, B, PartOfClassB_AccessedByA реализованы с отдельными файлами.
источник
Программирование на основе интерфейса помогает сделать код более гибким и более простым для тестирования. Классы реализации могут быть изменены без касания клиентского кода [Гибкость]. При тестировании кода можно заменить реальное программирование на основе oInterface, что делает код более гибким и более простым для тестирования. Классы реализации могут быть изменены без касания клиентского кода [Гибкость]. При тестировании кода можно заменить фактический объект фиктивными объектами [Testable] .bject на фиктивные объекты [Testable].
источник