Как другой популярный язык мог бы избежать использования фабричного шаблона при управлении такой же сложностью, как в Java / Java EE?

23

Фабричный шаблон (или, по крайней мере, использование FactoryFactory..) является предметом многих шуток, как здесь .

Помимо наличия подробных и «креативных» имен, таких как RequestProcessorFactoryFactory.RequestProcessorFactory , есть ли что-то принципиально неправильное в фабричном шаблоне, если вам нужно программировать на Java / C ++ и есть сценарий использования для Abstract_factory_pattern ?

Как другой популярный язык (например, Ruby или Scala ) мог бы избежать его использования при управлении аналогичной сложностью?

Причина, по которой я спрашиваю, состоит в том, что я вижу в основном критику фабрик, упомянутых в контексте экосистемы Java / Java EE , но они никогда не объясняют, как их решают другие языки / платформы.

senseiwu
источник
4
Проблема не в использовании фабрик, а в идеальном подходе. Это чрезмерное использование фабрик, которое особенно распространено в корпоративном Java-мире.
CodesInChaos
Очень хорошая шутка. Я хотел бы видеть функциональную интерпретацию языка получения инструментов для создания стойки для специй. Я думаю, это действительно привело бы его домой.
Патрик М

Ответы:

28

Ваш вопрос помечен как «Java», поэтому неудивительно, что вы спрашиваете, почему шаблон фабрики высмеивается: сама Java поставляется с красиво упакованными нарушениями этого шаблона.

Например, попробуйте загрузить документ XML из файла и выполнить к нему запрос XPath. Вам нужно что-то вроде 10 строк кода, чтобы настроить фабрики и строителей:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder(); 

Document xmlDocument = builder.parse(new FileInputStream("c:\\employees.xml"));
XPath xPath =  XPathFactory.newInstance().newXPath();

xPath.compile(expression).evaluate(xmlDocument);

Интересно, работали ли разработчики этого API когда-либо как разработчики или они просто читали книги и разбрасывали вещи? Я понимаю, что они просто не хотели писать синтаксический анализатор сами и оставили задачу другим, но это все равно делает уродливой реализацию.

Поскольку вы спрашиваете, что является альтернативой, здесь загружается файл XML в C #:

XDocument xml = XDocument.Load("c:\\employees.xml");
var nodes = xml.XPathSelectElements(expression);

Я подозреваю, что новоявленные Java-разработчики видят безумие Фабрики и думают, что это нормально, если гении, создавшие Java, сами их использовали.

Фабричные и любые другие шаблоны - это инструменты, каждый из которых подходит для определенной работы. Если вы применяете их на рабочих местах, они не подходят, потому что у вас обязательно будет некрасивый код.

Стен Петров
источник
1
Просто обратите внимание, что ваша альтернатива C # использует более новую LINQ to XML - более старая альтернатива DOM выглядела бы примерно так: XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression); (В целом, LINQ to XML гораздо проще в использовании, хотя в основном в других областях.) А вот статья KB Я нашел с помощью рекомендованного MS метода: support.microsoft.com/kb/308333
Боб
36

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

И это, несомненно, проистекает из того, как программируется (и шаблоны). Школьникам (часто называющим себя «учениками») говорят «создать X, используя шаблон Y», и после нескольких итераций этого считают, что это подход к любой проблеме программирования.
Таким образом, они начинают применять определенный шаблон, который им нравится в школе, против всего и вся, будь то уместно или нет.

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

Система имела базу данных, нормализованную до 5-й нормальной формы (я не шучу).

Система, которая по сути была немногим больше, чем адресная книга, которую можно было использовать для регистрации и отслеживания входящих и исходящих документов, имела кодовую базу 100 МБ в C ++ и более 50 таблиц базы данных. Печать 500 бланков писем может занять до 72 часов с использованием принтера, подключенного напрямую к компьютеру, на котором запущено программное обеспечение.

Вот почему люди издеваются над шаблонами, и особенно люди, сосредоточенно сосредоточенные на одном конкретном шаблоне.

jwenting
источник
40
Другая причина заключается в том, что некоторые из паттернов «Банды четырех» - просто действительно многословные хаки для вещей, которые можно сделать тривиально на языке с первоклассными функциями, что делает их неизбежными анти-паттернами. Шаблоны «Стратегия», «Наблюдатель», «Фабрика», «Команда» и «Метод шаблона» - это хаки для передачи функций в качестве аргументов; «Посетитель» - это хак, чтобы выполнить сопоставление с образцом для типа суммы / варианта / тегового объединения. Тот факт, что некоторые люди делают много шума из-за того, что буквально тривиально во многих других языках, заставляет людей издеваться над C ++, Java и подобными языками.
Доваль
7
Спасибо за этот анекдот. Не могли бы вы также рассказать немного о том, «каковы альтернативы»? часть вопроса, чтобы мы тоже не стали такими?
Филипп
1
@Doval Можете ли вы сказать мне, как вы возвращаете значение из выполненной команды или вызванной стратегии, когда вы можете только передать функцию? И если вы говорите замыкания, то имейте в виду, что это то же самое, что и создание класса, за исключением более явного.
Эйфорическая
2
@Euphoric Я не вижу проблемы, вы можете уточнить? В любом случае, они не совсем одно и то же. Можно также сказать, что объект с одним полем точно такой же, как указатель / ссылка на переменную, или что целое число точно такое же, как (действительное) перечисление. Практические различия: нет смысла сравнивать функции; Я еще не видел краткий синтаксис для анонимных классов; и все функции с одинаковыми аргументами и типами возвращаемых данных имеют одинаковый тип (в отличие от классов / интерфейсов с разными именами, которые являются разными типами, даже если они идентичны.)
Doval,
2
@ Эйфорика Правильно. Это будет считаться плохим функциональным стилем, если есть способ выполнить работу без побочных эффектов. Простая альтернатива - использовать объединение типа суммы / тега для возврата одного из N видов значений. В любом случае, давайте предположим, что практического пути нет; это не то же самое, что класс. На самом деле это интерфейс (в смысле ООП). Нетрудно понять, считаете ли вы, что любая пара функций с одинаковыми сигнатурами будет взаимозаменяемой, в то время как два класса никогда не взаимозаменяемы, даже если они имеют одинаковое содержимое.
Доваль
17

Фабрики имеют много преимуществ, которые позволяют в некоторых ситуациях создавать элегантные проекты. Одним из них является то, что вы можете установить свойства объектов, которые вы позже хотите создать, в одном месте, создав фабрику, а затем передать эту фабрику. Но часто вам не нужно этого делать. В этом случае использование Factory просто добавляет дополнительную сложность, фактически не давая вам ничего взамен. Давайте возьмем эту фабрику, например:

WidgetFactory redWidgetFactory = new ColoredWidgetFactory(COLOR_RED);
Widget widget = redWidgetFactory.create();

Одной альтернативой шаблону Factory является очень похожий шаблон Builder. Основное отличие состоит в том, что свойства объектов, созданных Фабрикой, устанавливаются при инициализации Фабрики, тогда как Builder инициализируется с состоянием по умолчанию, а все свойства устанавливаются впоследствии.

WidgetBuilder widgetBuilder = new WidgetBuilder();
widgetBuilder.setColor(COLOR_RED);
Widget widget = widgetBuilder.create();

Но когда ваша проблема заключается в переподготовке, замена Фабрики на Строителя, скорее всего, не является улучшением.

Самая простая замена для любого шаблона - это, конечно, создание экземпляров объекта с помощью простого конструктора с newоператором:

Widget widget = new ColoredWidget(COLOR_RED);

Однако конструкторы имеют существенный недостаток в большинстве объектно-ориентированных языков: они должны возвращать объект именно этого класса и не могут возвращать подтип.

Когда вам нужно выбрать подтип во время выполнения, но вы не хотите создавать для этого целый новый класс Builder или Factory, вы можете использовать вместо этого метод factory. Это статический метод класса, который возвращает новые экземпляры этого класса или одного из его подклассов. Фабрика, которая не поддерживает никакого внутреннего состояния, часто может быть заменена таким фабричным методом:

 Widget widget = Widget.createColoredWidget(COLOR_RED); // returns an object of class RedColoredWidget

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

Philipp
источник
2
Фабрика часто также может быть настроена после создания. Основное отличие, afaik, от компоновщика заключается в том, что компоновщик обычно используется для создания одного сложного экземпляра, а фабрики - для создания многих похожих экземпляров.
Головоногий
1
спасибо (+1), но ответ не объясняет, как это решат другие PL, тем не менее, спасибо за указание на ссылки на метод Java 8.
Сенсейву
1
Я часто думал, что в объектно-ориентированной среде имеет смысл ограничить фактическую конструкцию объекта соответствующим типом, и это foo = new Bar(23);будет эквивалентно foo = Bar._createInstance(23);. Объект должен иметь возможность надежно запрашивать свой собственный тип, используя закрытый getRealType()метод, но объекты должны иметь возможность назначать супертип, который должен возвращаться при внешних вызовах getType(). Внешний код не должен заботиться о том, new String("A")возвращает ли вызов на самом деле экземпляр String[в отличие, например, от экземпляра SingleCharacterString].
суперкат
1
Я использую множество фабрик статических методов, когда мне нужно выбрать подкласс на основе контекста, но различия должны быть непрозрачными для вызывающей стороны. Например, у меня есть серия классов изображений, которые содержат, draw(OutputStream out)но генерируют немного разные HTML, фабрика статических методов создает правильный класс для ситуации, и тогда вызывающая сторона может просто использовать метод draw.
Михаил Шопсин
1
@supercat, возможно, вам будет интересно взглянуть на цель-c. Там конструирование объектов обычно состоит из простых вызовов методов [[SomeClass alloc] init]. Можно вернуть совершенно другой экземпляр класса или другой объект (например, кэшированное значение)
axelarge
3

Фабрики того или иного типа встречаются практически в любом объектно-ориентированном языке при соответствующих обстоятельствах. Иногда вам просто нужно выбрать способ создания объекта на основе простого параметра, например строки.

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

Что меня поразило, когда я выучил Scala, и я не знаю Ruby, но считаю, что это почти так же, так это то, что язык достаточно выразителен, так что программисты не всегда пытаются перенести «слесарную» работу во внешние файлы конфигурации , Вместо того, чтобы поддаваться искушению создать фабрики-фабрики для соединения ваших классов в различных конфигурациях, в Scala вы используете объекты со смешанными чертами. Кроме того, относительно просто создавать простые DSL на языке для задач, которые часто слишком перегружены в Java.

Кроме того, как отмечали другие комментарии и ответы, замыкания и функции первого класса устраняют необходимость во многих шаблонах. По этой причине я считаю, что многие антишаблоны начнут исчезать, поскольку C ++ 11 и Java 8 получат широкое распространение.

Карл Билефельдт
источник
спасибо (+1). Ваш ответ объясняет некоторые мои сомнения относительно того, как Scala будет этого избегать. Честно говоря, не думаете ли вы, что однажды (если) Scala станет хотя бы наполовину менее популярной, чем Java на уровне предприятия, появятся армии фреймворков, шаблонов, серверов платных приложений и т. Д.?
Senseiwu
Я думаю, что если Scala обгонит Java, это будет потому, что люди предпочитают «способ Scala» для разработки программного обеспечения. Что мне кажется более вероятным, так это то, что Java продолжает принимать все больше функций, побуждающих людей переходить на Scala, таких как дженерики Java 5 и лямбды и потоки Java 8, что, как мы надеемся, в конечном итоге приведет к тому, что программисты откажутся от анти-паттернов, возникающих, когда те функции не были доступны. Всегда будут приверженцы FactoryFactory, независимо от того, насколько хороши языки. Очевидно, что некоторым людям нравятся эти архитектуры, или они не будут такими распространенными.
Карл Билефельдт
2

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

В частности, проблема Java с фабриками заключается в том, что в Java а) конструкторы не являются функциями и б) функции не являются гражданами первого класса.

Представьте, что вы могли бы написать что-то вроде этого (пусть это Functionбудет хорошо известный интерфейс JRE )

// Framework code
public Node buildTree(Function<BranchNode, Node, Node> branchNodeFactory) {
    Node current = nextNode();
    while (hasNextNode()) {
        current = branchNodeFactory.call(current, nextNode());
    }
    return current
}

// MyDataNode.java
public class MyBranchNode implements BranchNode {

    public MyBranchNode(Node left, Node right) { ... }
}

// Client code
Node root = buildTree(MyBranchNode::new);

Видите, нет фабричных интерфейсов или классов. Многие динамические языки имеют первоклассные функции. Увы, это невозможно в Java 7 и более ранних версиях (реализация того же самого на фабриках оставлена ​​читателю в качестве упражнения).

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

Cephalopod
источник
2
это даже не пытается ответить на вопрос: «каковы альтернативы?»
комнат
1
Прочитайте второй абзац, и вы попадете в раздел «как это делают другие языки». (PS: другой ответ также не показывает альтернативы)
Головоногий
4
@gnat Не говорит ли он косвенно, что альтернативой является использование первоклассных функций? Если вам нужен черный ящик, который может создавать объекты, ваши параметры - это либо фабрика, либо функция, а фабрика - это просто скрытая функция.
Доваль
3
«Во многих динамических языках» немного вводит в заблуждение, поскольку на самом деле нужен язык с первоклассными функциями. «Динамический» ортогональн к этому.
Андрес Ф.
Правда, но это свойство часто встречается в динамических языках. Я упомянул о необходимости первоклассной функции в начале моего ответа.
Головоногий