Является ли «программа для интерфейсов» общим принципом проектирования в проектах C ++? - PullRequest
4 голосов
/ 28 октября 2010

Я много читал о «программе к интерфейсам» и «инверсии управления» в последние дни. В основном в контексте с языком Java. Мой вопрос, если это также распространенная практика в разработке C ++. Каковы преимущества? Какие недостатки?

Стоит ли применять для небольших проектов (например, 15-20 классов)?

Ответы [ 10 ]

7 голосов
/ 28 октября 2010

Да, это довольно распространено, но не в той форме, которую вы могли ожидать.

В Java интерфейс формализован и явен, а программирование для интерфейса означает реализацию этого конкретного interface.

В C ++ тоже иногда делается то же самое (хотя используются абстрактные базовые классы, а не интерфейсы), но другой распространенный способ сделать это в C ++ - это шаблоны, где интерфейс неявный.

Например, все стандартные алгоритмы библиотеки работают с «интерфейсом» итератора, за исключением того, что такой интерфейс никогда не определяется в коде.Это соглашение, и ничего более.

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

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

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

6 голосов
/ 28 октября 2010

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

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

Это представляет проблему;Конечно, ваш класс Depender может сказать, что ему нужен IDoSomething вместо DoerClass, но если Depender знает, как создать DoerClass для использования в качестве IDoSomething, вы ничего не получили;Если вы хотите заменить DoerClass на BetterDoer, вы все равно должны изменить код Depender.Решение состоит в том, чтобы дать ответственность за предоставление классу экземпляра зависимости третьей стороне, Создателю.Класс, выбранный для этого, зависит от контекста.Если у класса, естественно, есть и Depender, и DoerClass, это очевидный выбор для их объединения.Это часто имеет место, когда у вас есть один класс, который имеет две вспомогательные зависимости, и одна зависимость также нуждается в другой.В других случаях вы можете создать Factory, которая существует для предоставления вызывающей стороне экземпляра определенного объекта, предпочтительно со всеми зависимостями, подключенными.

Если у вас есть несколько взаимозависимых классов или многоуровневых зависимостей, вы можете рассмотреть среду IoC.Контейнеры IoC предназначены для фабрик, а репозитории для DAO;они знают, как получить полностью гидратированный экземпляр ЛЮБОГО класса, требующего одну или несколько зависимостей, например, репозиторий может создать любой полностью гидратированный доменный объект из данных в БД.Это достигается тем, что ему сообщают, какой конкретный класс следует использовать для заполнения зависимости в определенной ситуации, и когда его просят предоставить класс, он создает экземпляр этого класса, предоставляя экземпляры требуемых зависимостей (и зависимостей зависимостей).Это может разрешить шаблоны, где класс A зависит от B, который зависит от C, но A не может знать о C. Инфраструктура IoC знает обо всех трех и создаст экземпляр B, даст ему новый C, а затем передаст B новомуA.

4 голосов
/ 28 октября 2010

Абсолютно!Инкапсуляция является основной частью философии ООП.Сохраняя реализацию отдельно от интерфейса класса, ваш код становится намного более универсальным.Например, если у меня был класс Vector, и я хотел изменить внутреннее представление с пары x и y на длину и направление (допустим, это для эффективности), то изменил только несколько функций-членов, которые обрабатываютреализация на порядок проще, чем поиск по 100 исходным файлам для каждого класса, который зависит от реализации классов.

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

1 голос
/ 28 октября 2010

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

1 голос
/ 28 октября 2010

В C ++ есть больше интерфейсов, чем в Java. Простой ответ: да , это обычная практика. Должны ли вы следовать этому в своем небольшом проекте, зависит от случая. Судите за каждый класс, а не за проект в целом. Как правило, если это не сломано, не исправляйте это.

Тем не менее, в C ++ у вас есть два вида интерфейсов: тот, который поддерживает полиморфизм во время выполнения и тот, который поддерживает полиморфизм во время компиляции. Полиморфизм времени исполнения очень похож на то, что вы знаете из Java. Полиморфизм времени компиляции происходит от использования шаблонов.

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

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

1 голос
/ 28 октября 2010

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

Я бы не сказал, что это распространено, но это также не редкость - возможно, где-то посередине?COM компании Microsoft основан на концепции.

См. Этот вопрос для получения дополнительной информации о том, как это делается: Как вы объявляете интерфейс в C ++?

0 голосов
/ 25 октября 2011

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

Этот стиль мышления считается хорошей практикой на любом языке программирования, даже если вы явно не программируете интерфейсы.(см. также ТВЕРДЫЕ принципы).

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

0 голосов
/ 28 октября 2010

Посмотрите на дизайн стандартной библиотеки шаблонов. Например, у msdn channel9 есть несколько хороших видеоуроков, которые объясняют дизайн (и реализацию MSFT) STL.

http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Introduction-to-STL-with-Stephan-T-Lavavej

0 голосов
/ 28 октября 2010

Интерфейсы стали популярными в Java, и в настоящее время в C # до того, как интерфейсы Object-Oriented Programming уже существуют, самый полный язык, когда-либо созданный (на мой взгляд) C ++, не вырос на основе интерфейсов.Основная потребность в создании интерфейсов возникла для решения проблемы наличия множественного наследования (c ++ допускает множественное наследование).

В C ++ определение интерфейсов не является расширенной практикой (, но их можно моделировать с помощью чисто абстрактных классов ).Также существует общепринятая практика определения файла, содержащего объявления (.H) и другие, содержащие реализацию (.CPP), вероятно, это в сочетании с чисто абстрактным предложением служит источником вдохновения для создания интерфейсов на современных языках OO.

0 голосов
/ 28 октября 2010

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

Пользователь библиотеки не хотел бы, чтобы интерфейс библиотеки постоянно менялся.Представьте, что вам пришлось переписывать свои программы, потому что авторы библиотеки C решили переопределить «printf».Или что, если библиотека Java решила переопределить интерфейс toString?

Так что да, «программа для интерфейсов» используется в C ++.

...