Абстрактные классы против шаблонов - хорошие практики
Допустим, у меня есть некоторый класс, который представляет алгоритм, и этот алгоритм требует чего-то особенного из данных (например, некоторая функция-член).
В примере мы можем сделать:
<<interface>>
+------------------------+ +------------+
| Algorithm | <<uses>> | Data |
+------------------------+-------------->+------------+
| + doJob(inData : Data) | | +getPixel()|
+------------------------+ +------------+
И мы можем заставить пользователя Algorithm наследовать от Data каждый раз, когда он хочет использовать алгоритм класса. Мы также можем сделать шаблон:
template<typename T>
doJob(T&& inputData){
//implementation
}
(функция без класса, чтобы упростить вещи)
И мы заставляем нашего клиента создавать классы, у которых есть методы с собственным именем, но мы не заставляем его реализовывать наш абстрактный класс (интерфейс на разных языках) (возможно, немного лучшую производительность?)
И мой вопрос:
Какой подход лучше?
Если у нас есть выбор, следует ли нам реализовывать вещи шаблонным или абстрактным способом в библиотеке?
Есть ли причина для стандарта не определять некоторые стандартные "Интерфейсы", такие как std::container или std::factory (только примеры)?
2 ответа
На самом деле у вас есть более одного вопроса, поэтому давайте ответим на них один за другим:
Какой подход лучше?
Ни один не лучше в целом. У каждого есть свои сильные и слабые стороны. Но вы подошли к интересному моменту: на более абстрактном уровне эти два практически одинаковы.
Если у нас есть выбор, следует ли нам реализовывать вещи шаблонным или абстрактным способом в библиотеке?
С шаблонами вы получаете:
В общем, более быстрое исполнение. Это может быть намного быстрее, потому что много встраивания, а затем можно провести оптимизацию. OTOH, с продвинутым компилятором / компоновщиком де-виртуализации и функциями, которые не могут быть сильно встроены / оптимизированы, вы можете получить почти такую же скорость.
Более медленные времена компиляции. Это может быть намного медленнее, особенно если вы идете по пути "необычного шаблона-мета-программирования".
Хуже ошибки компилятора. Они могут быть намного хуже, особенно если вы идете по пути "модного шаблонного метапрограммирования". Когда C++ получает поддержку концепций, следует избегать этого.
Если вы разрабатываете это тщательно, улучшенная безопасность типов. OTOH, если вы не будете осторожны, вы в конечном итоге будете печатать на утке хуже, чем Smalltalk. Концепции могут быть инструментом, который может помочь и здесь.
С виртуальными функциями / интерфейсами вы получаете:
- Разделенный дизайн, где, если вы будете осторожны, изменения из одного файла не потребуют повторной компиляции других, а время компиляции может быть намного быстрее.
- Полиморфизм во время выполнения, что означает, что вы можете динамически загружать код (это не так просто, как кажется, но это возможно)
- Что-то, что выглядит более знакомым для тех, кто имеет опыт в ОО.
Есть ли причина для стандарта не определять некоторые стандартные "Интерфейсы", такие как std::container или std::factory (только примеры)?
Я думаю, можно найти много "низкоуровневых" причин, но фундаментальная причина - производительность. Таким образом, STL был спроектирован так, чтобы быть "настолько быстрым, насколько это возможно", а поставить некоторые (полезные) интерфейсы "сверху, если это" сейчас практически невозможно.
Кажется, это вопрос, основанный на мнении. Лучший способ заставить клиента выполнить свои обязательства - заставить его подписать контракт, этот контакт является интерфейсом.