Заводской шаблон. Когда использовать заводские методы?

Когда рекомендуется использовать фабричные методы внутри объекта вместо classа Factory?

Мне нравится думать о дизайне pattens с точки зрения моих classов, которые являются «людьми», и шаблоны – это способы, которыми люди общаются друг с другом.

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

Поэтому, когда им нужен новый сотрудник, они называют агентство по найму и сообщают им, что им нужно. Теперь, чтобы нанять кого-то, вам нужно знать много вещей – преимущества, подтверждение приемлемости и т. Д. Но человеку, нанимающему, не нужно знать ничего из этого – агентство по найму справляется со всем этим.

Точно так же использование Factory позволяет потребителю создавать новые объекты, не зная подробностей о том, как они созданы или каковы их зависимости, – они должны только предоставить информацию, которую они действительно хотят.

public interface IThingFactory { Thing GetThing(string theString); } public class ThingFactory : IThingFactory { public Thing GetThing(string theString) { return new Thing(theString, firstDependency, secondDependency); } } 

Таким образом, теперь потребитель ThingFactory может получить Thing, не зная о зависимостях Thing, за исключением строковых данных, которые поступают от потребителя.

Фабричные методы следует рассматривать как альтернативу конструкторам – в основном, когда конструкторы недостаточно выразительны, т.е.

 class Foo{ public Foo(bool withBar); } 

не столь выразителен, как:

 class Foo{ public static Foo withBar(); public static Foo withoutBar(); } 

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

Одна из ситуаций, когда я лично нахожу отдельные classы Factory, имеет смысл, когда конечный объект, который вы пытаетесь создать, полагается на несколько других объектов. Например, в PHP: предположим, что у вас есть объект House , который, в свою очередь, имеет объект Kitchen и LivingRoom , а объект LivingRoom имеет TV объект.

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

Альтернативой является сделать следующее (инъекция зависимости, если вам нравится этот термин):

 $TVObj = new TV($param1, $param2, $param3); $LivingroomObj = new LivingRoom($TVObj, $param1, $param2); $KitchenroomObj = new Kitchen($param1, $param2); $HouseObj = new House($LivingroomObj, $KitchenroomObj); 

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

 class HouseFactory { public function create() { $TVObj = new TV($param1, $param2, $param3); $LivingroomObj = new LivingRoom($TVObj, $param1, $param2); $KitchenroomObj = new Kitchen($param1, $param2); $HouseObj = new House($LivingroomObj, $KitchenroomObj); return $HouseObj; } } $houseFactory = new HouseFactory(); $HouseObj = $houseFactory->create(); 

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

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

Будем говорить о «заводском методе»:

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

Теперь дело в том, что примерно так же может быть достигнуто с использованием самой фабричной модели, но один огромный недостаток будет внедряться в систему, если шаблон фабрики будет использоваться для вышеописанной проблемы, это значит, что ваша логика разбиения различных объектов (объектов подclass) будет быть конкретным для некоторых бизнес-условий, поэтому в будущем, когда вам нужно расширить функциональность вашей библиотеки для других платформ (более технически вам нужно добавить дополнительные подclassы базового интерфейса или абстрактного classа, чтобы фабрика вернула эти объекты также в дополнение к существующим основанные на некоторых динамических параметрах), то каждый раз, когда вам нужно изменить (расширить) логику фабричного classа, которая будет дорогостоящей операцией, а не хорошей с точки зрения дизайна. С другой стороны, если для выполнения одной и той же функции будет использоваться шаблон «factory method», вам просто нужно создать дополнительную функциональность (подclassы) и получить ее динамически путем инжекции, которая не требует изменений в вашем базовом коде.

 interface Deliverable { /*********/ } abstract class DefaultProducer { public void taskToBeDone() { Deliverable deliverable = factoryMethodPattern(); } protected abstract Deliverable factoryMethodPattern(); } class SpecificDeliverable implements Deliverable { /***SPECIFIC TASK CAN BE WRITTEN HERE***/ } class SpecificProducer extends DefaultProducer { protected Deliverable factoryMethodPattern() { return new SpecificDeliverable(); } } public class MasterApplicationProgram { public static void main(String arg[]) { DefaultProducer defaultProducer = new SpecificProducer(); defaultProducer.taskToBeDone(); } } 

Они также полезны, когда вам нужны несколько «конструкторов» с одним и тем же типом параметров, но с другим поведением.

Рекомендуется использовать фабричные методы внутри объекта, когда:

  1. Класс объекта не знает, какие точные подclassы он должен создать
  2. Класс Object разработан таким образом, что создаваемые объекты были определены подclassами
  3. Класс объекта делегирует свои обязанности вспомогательным подclassам и не знает, какой именно class возьмет на себя эти обязанности

Рекомендуется использовать абстрактный заводский class, когда:

  1. Ваш объект не должен зависеть от того, как его внутренние объекты создаются и разрабатываются
  2. Группа связанных объектов должна использоваться вместе, и вам необходимо выполнить это ограничение
  3. Объект должен быть настроен одним из нескольких возможных семейств связанных объектов, которые будут частью вашего родительского объекта
  4. Требуется разделить дочерние объекты, отображающие только интерфейсы, но не реализацию

UML из

введите описание изображения здесь

Продукт: Определяет интерфейс объектов, создаваемых методом Factory.

ConcreteProduct: реализует интерфейс продукта

Создатель: объявляет заводский метод

ConcreateCreator: реализует метод Factory для возврата экземпляра ConcreteProduct

Заявление о проблемах: создайте фабрику игр с помощью Factory Methods, которая определяет интерфейс игры.

Фрагмент кода:

 import java.util.HashMap; /* Product interface as per UML diagram */ interface Game{ /* createGame is a complex method, which executes a sequence of game steps */ public void createGame(); } /* ConcreteProduct implementation as per UML diagram */ class Chess implements Game{ public Chess(){ } public void createGame(){ System.out.println("---------------------------------------"); System.out.println("Create Chess game"); System.out.println("Opponents:2"); System.out.println("Define 64 blocks"); System.out.println("Place 16 pieces for White opponent"); System.out.println("Place 16 pieces for Black opponent"); System.out.println("Start Chess game"); System.out.println("---------------------------------------"); } } class Checkers implements Game{ public Checkers(){ } public void createGame(){ System.out.println("---------------------------------------"); System.out.println("Create Checkers game"); System.out.println("Opponents:2 or 3 or 4 or 6"); System.out.println("For each opponent, place 10 coins"); System.out.println("Start Checkers game"); System.out.println("---------------------------------------"); } } class Ludo implements Game{ public Ludo(){ } public void createGame(){ System.out.println("---------------------------------------"); System.out.println("Create Ludo game"); System.out.println("Opponents:2 or 3 or 4"); System.out.println("For each opponent, place 4 coins"); System.out.println("Create two dices with numbers from 1-6"); System.out.println("Start Ludo game"); System.out.println("---------------------------------------"); } } /* Creator interface as per UML diagram */ interface IGameFactory { public Game getGame(String gameName); } /* ConcreteCreator implementation as per UML diagram */ class GameFactory implements IGameFactory { HashMap games = new HashMap(); /* Since Game Creation is complex process, we don't want to create game using new operator every time. Instead we create Game only once and store it in Factory. When client request a specific game, Game object is returned from Factory instead of creating new Game on the fly, which is time consuming */ public GameFactory(){ games.put(Chess.class.getName(),new Chess()); games.put(Checkers.class.getName(),new Checkers()); games.put(Ludo.class.getName(),new Ludo()); } public Game getGame(String gameName){ return games.get(gameName); } } public class NonStaticFactoryDemo{ public static void main(String args[]){ if ( args.length < 1){ System.out.println("Usage: java FactoryDemo gameName"); return; } GameFactory factory = new GameFactory(); Game game = factory.getGame(args[0]); if ( game != null ){ game.createGame(); System.out.println("Game="+game.getClass().getName()); }else{ System.out.println(args[0]+ " Game does not exists in factory"); } } } 

вывод:

 java NonStaticFactoryDemo Chess --------------------------------------- Create Chess game Opponents:2 Define 64 blocks Place 16 pieces for White opponent Place 16 pieces for Black opponent Start Chess game --------------------------------------- Game=Chess 

В этом примере показан class Factory , реализующий FactoryMethod .

  1. Game - это интерфейс для всех типов игр. Он определяет сложный метод: createGame()

  2. Chess, Ludo, Checkers - это разные варианты игр, которые обеспечивают реализацию createGame()

  3. public Game getGame(String gameName) - FactoryMethod в classе IGameFactory

  4. GameFactory предварительно создает разные типы игр в конструкторе. Он реализует заводской метод IGameFactory .

  5. Название игры передается как аргумент командной строки NotStaticFactoryDemo

  6. getGame в GameFactory принимает имя игры и возвращает соответствующий Game объект.

Фабрика:

Создает объекты, не подвергая клиентскую логику создания.

FactoryMethod

Определите интерфейс для создания объекта, но пусть подclassы решают, какой class необходимо создать. Метод Factory позволяет создать экземпляр classа для подclassов

Случай использования:

Когда использовать: Client не знает, какие конкретные classы он должен будет создавать во время выполнения, но просто хочет получить class, который будет выполнять эту работу.

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

Фабричные classы полезны, когда возвращаемый объект имеет закрытый конструктор, когда разные фабричные classы задают разные свойства на возвращаемом объекте или когда конкретный тип фабрики связан с возвращаемым конкретным типом.

WCF использует classы ServiceHostFactory для извлечения объектов ServiceHost в разных ситуациях. Стандартный ServiceHostFactory используется IIS для извлечения экземпляров ServiceHost для файлов .svc , но WebScriptServiceHostFactory используется для служб, которые возвращают сериализации для клиентов JavaScript. У ADO.NET Data Services есть собственный специальный DataServiceHostFactory, и ASP.NET имеет ApplicationServicesHostFactory, поскольку его службы имеют частные конструкторы.

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

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

Теперь возникает новое представление о том, что объект Order не может быть создан без ассоциации Customer (новая зависимость). Теперь у вас есть следующие соображения.

1- Вы создаете перегрузку конструктора, которая будет работать только для новых реализаций. (Недопустимо). 2- Вы меняете подписи Order () и меняете каждую инвокацию. (Не хорошая практика и настоящая боль).

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

Согласно источнику веб-сайта, его намерения заключаются в следующем:

  • Определите интерфейс для создания объекта, но пусть подclassы решают, какой class следует создавать. Фабричный метод позволяет создать экземпляр classа для подclassов.

  • Определение «виртуального» конструктора.

  • Новый оператор считал вредным.

Пример того, как это можно использовать:

 abstract class AbstractFactoryMethod { abstract function makePHPBook($param); } class OReillyFactoryMethod extends AbstractFactoryMethod { function makePHPBook($param) { $book = NULL; switch ($param) { case "us": $book = new OReillyPHPBook(); break; // Other classes... case "other": $book = new SamsPHPBook(); break; default: $book = new OReillyPHPBook(); break; } return $book; } 

И затем тестирование:

 function testFactoryMethod($factoryMethodInstance) { $phpUs = $factoryMethodInstance->makePHPBook("us"); echo 'us php Author: '.$phpUs->getAuthor(); echo 'us php Title: '.$phpUs->getTitle(); } echo 'Testing OReillyFactoryMethod'; $factoryMethodInstance = new OReillyFactoryMethod(); testFactoryMethod($factoryMethodInstance); 

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

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

Я уподобляю фабрики концепции библиотек. Например, вы можете иметь библиотеку для работы с числами и другую для работы с фигурами. Вы можете хранить функции этих библиотек в логически названных каталогах как Numbers или Shapes . Это типичные типы, которые могут включать целые числа, поплавки, добулы, длинные или прямоугольники, круги, треугольники, пятиугольники в случае фигур.

Заводчик-петтер использует polymorphism, инъекцию зависимостей и инверсию управления.

Заявленная цель Factory Patterns: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Предположим, что вы строите операционную систему или платформу, и вы строите все дискретные компоненты.

Вот простой пример концепции Factory Pattern в PHP. Я не могу быть 100% от всего этого, но он предназначен для простого примера. Я не эксперт.

 class NumbersFactory { public static function makeNumber( $type, $number ) { $numObject = null; $number = null; switch( $type ) { case 'float': $numObject = new Float( $number ); break; case 'integer': $numObject = new Integer( $number ); break; case 'short': $numObject = new Short( $number ); break; case 'double': $numObject = new Double( $number ); break; case 'long': $numObject = new Long( $number ); break; default: $numObject = new Integer( $number ); break; } return $numObject; } } /* Numbers interface */ abstract class Number { protected $number; public function __construct( $number ) { $this->number = $number; } abstract public function add(); abstract public function subtract(); abstract public function multiply(); abstract public function divide(); } /* Float Implementation */ class Float extends Number { public function add() { // implementation goes here } public function subtract() { // implementation goes here } public function multiply() { // implementation goes here } public function divide() { // implementation goes here } } /* Integer Implementation */ class Integer extends Number { public function add() { // implementation goes here } public function subtract() { // implementation goes here } public function multiply() { // implementation goes here } public function divide() { // implementation goes here } } /* Short Implementation */ class Short extends Number { public function add() { // implementation goes here } public function subtract() { // implementation goes here } public function multiply() { // implementation goes here } public function divide() { // implementation goes here } } /* Double Implementation */ class Double extends Number { public function add() { // implementation goes here } public function subtract() { // implementation goes here } public function multiply() { // implementation goes here } public function divide() { // implementation goes here } } /* Long Implementation */ class Long extends Number { public function add() { // implementation goes here } public function subtract() { // implementation goes here } public function multiply() { // implementation goes here } public function divide() { // implementation goes here } } $number = NumbersFactory::makeNumber( 'float', 12.5 ); 

Пример AbstractFactory.

  TypeImpl type = new TypeImpl<>(); type.addType("Condition"); type.addType("Hazardous"); AbstractTypeFactory tags = new AbstractTypeFactory(type) { @Override public Tag create(String string) { String tp = type.find(string); switch (tp) { case "Hazardous": return new HazardousTag(); case "Condition": return new ConditionTag(); default: return null; } } }; Tag tagHazardous = tags.create("Hazardous"); Tag tagCondition = tags.create("Condition"); } 

если вы хотите создать другой объект с точки зрения использования. Это полезно.

 public class factoryMethodPattern { static String planName = "COMMERCIALPLAN"; static int units = 3; public static void main(String args[]) { GetPlanFactory planFactory = new GetPlanFactory(); Plan p = planFactory.getPlan(planName); System.out.print("Bill amount for " + planName + " of " + units + " units is: "); p.getRate(); p.calculateBill(units); } } abstract class Plan { protected double rate; abstract void getRate(); public void calculateBill(int units) { System.out.println(units * rate); } } class DomesticPlan extends Plan { // @override public void getRate() { rate = 3.50; } } class CommercialPlan extends Plan { // @override public void getRate() { rate = 7.50; } } class InstitutionalPlan extends Plan { // @override public void getRate() { rate = 5.50; } } class GetPlanFactory { // use getPlan method to get object of type Plan public Plan getPlan(String planType) { if (planType == null) { return null; } if (planType.equalsIgnoreCase("DOMESTICPLAN")) { return new DomesticPlan(); } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) { return new CommercialPlan(); } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) { return new InstitutionalPlan(); } return null; } } 

Любой class, откладывающий создание объекта к его подclassу для объекта, с которым он должен работать, можно рассматривать как пример шаблона Factory.

Я подробно упомянул в другом ответе на https://stackoverflow.com/a/49110001/504133

  • Текстовая анимация WPF Marquee
  • Фабричный, абстрактный заводский и заводской метод
  • Давайте будем гением компьютера.