Почему этот ShapeFactory использует условные операторы, чтобы определить, какой объект нужно создать. Разве нам не нужно изменять ShapeFactory, если мы хотим добавить другие классы в будущем? Почему это не нарушает принцип открытого закрытого?
Почему этот ShapeFactory использует условные операторы, чтобы определить, какой объект нужно создать. Разве нам не нужно изменять ShapeFactory, если мы хотим добавить другие классы в будущем? Почему это не нарушает принцип открытого закрытого?
Ответы:
Обычная объектно-ориентированная мудрость - избегать if
операторов и заменять их динамической диспетчеризацией переопределенных методов в подклассах абстрактного класса. Все идет нормально.
Но смысл фабричного паттерна состоит в том, чтобы избавить вас от необходимости знать об отдельных подклассах и работать только с абстрактным суперклассом . Идея состоит в том, что фабрика лучше вас знает, какой конкретный класс нужно создать, и вам будет лучше работать только с методами, опубликованными суперклассом. Это часто верно и ценный образец.
Следовательно, нет никакого способа, которым написание фабричного класса могло бы отказаться от if
утверждений. Это переместит бремя выбора определенного класса на вызывающего, что именно то, что шаблон должен избегать. Не все принципы являются абсолютными (фактически, ни один принцип не является абсолютным), и если вы используете этот шаблон, вы предполагаете, что выгода от него больше, чем выгода от неиспользования if
.
if
s. Посмотрите ответ @ BЈовић для простого примера того, как этого добиться. Downvoted.
В примере, вероятно, используется условный оператор, потому что он самый простой. Более сложная реализация может использовать карту или конфигурацию или (если вы хотите быть по-настоящему модным) какой-то реестр, где классы могут регистрировать себя. Однако нет ничего плохого в использовании условного выражения, если число классов мало и изменяется нечасто.
Расширение условного добавления поддержки нового подкласса в будущем действительно будет, строго говоря, нарушением принципа открытого / закрытого. «Правильным» решением было бы создание новой фабрики с таким же интерфейсом. При этом соблюдение принципа O / C всегда следует сопоставлять с другими принципами проектирования, такими как KISS и YAGNI.
Тем не менее, отображаемый код является явно примером кода, который разработан, чтобы показать концепцию фабрики и ничего больше. Например, это действительно плохой стиль - возвращать ноль, как в примере, но более сложная обработка ошибок просто затеняет суть. Пример кода не является производственным кодом качества, так как вы не должны этого ожидать.
Сам шаблон не нарушает принцип Open / Closed (OCP). Тем не менее, мы нарушаем OCP, когда мы используем шаблон неправильно.
Простой ответ на этот вопрос заключается в следующем:
В приведенном примере базовая функциональность поддерживает три формы: круг, прямоугольник и квадрат. Предположим, что в будущем вам потребуется поддержка Triangle, Pentagon и Hexagon. Чтобы сделать это БЕЗ нарушения OCP, вы должны создать дополнительную фабрику для поддержки ваших новых фигур (давайте назовем ее AdvancedShapeFactory
), а затем использовать AbstractFactory, чтобы решить, какую фабрику вам нужно создать, чтобы создавать любые нужные фигуры.
Если вы говорите о шаблоне абстрактной фабрики, то принятие решений часто не в самой фабрике, а в коде приложения. Именно этот код выбирает конкретную фабрику для создания экземпляра и передачи клиентскому коду, который будет использовать объекты, созданные фабрикой. Смотрите конец примера Java здесь: https://en.wikipedia.org/wiki/Abstract_factory_pattern
Принятие решения не обязательно подразумевает if
заявления. Он может прочитать конкретный тип Factory из файла конфигурации, извлечь его из структуры карты и т. Д.
Если вы думаете об Open-Close на уровне класса с помощью этой фабрики, вы создаете другой класс в вашей системе Open-Close, например, если у вас есть другой класс, который принимает один Shape и вычисляет площадь (типичный пример), этот класс является OpenClose, потому что он может рассчитать площадь для новых типов фигур без изменений. Затем у вас есть другой класс, который рисует фигуру, другой класс, который принимает N фигур и возвращает больший, и вы можете думать в целом, что другие классы в вашей системе, которые работают с фигурами, являются Open-Close (по крайней мере, для фигур). Глядя на конструкцию, фабрика позволяет открывать-закрывать остальную часть системы и, конечно, сама фабрика НЕ ОТКРЫВАЕТ-закрывает.
Конечно, вы также можете сделать эту фабрику открытой-закрытой, с помощью некоторой динамической загрузки, и вся ваша система может быть открытой-закрытой (вы можете добавить новые фигуры, например, выбрасывая банку в путь к классам). Вы должны оценить, стоит ли эта дополнительная сложность в зависимости от системы, которую вы строите, не все системы нуждаются в подключаемых функциях, и не вся система должна быть полностью Open-Close.
Принцип открытого-закрытого типа, как принцип подстановки Лискова, применяется к деревьям классов, к иерархиям наследования. В вашем примере фабричный класс не входит в семейное дерево классов, которые он создает, поэтому он не может нарушать эти правила. Было бы нарушение, если бы ваш GetShape (или, более точно, CreateShape) был реализован в базовом классе Shape.
Все зависит от того, как вы это реализуете. Вы можете использовать std::map
для хранения указателей на функции, создающие объекты. Тогда принцип открытия / закрытия не нарушается. Или переключатель / чехол.
В любом случае, если вам не нравится шаблон фабрики, вы всегда можете использовать внедрение зависимостей.