Заводская модель. Когда использовать фабричные методы? - PullRequest
252 голосов
/ 16 сентября 2008

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

Ответы [ 15 ]

365 голосов
/ 12 марта 2010

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

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

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

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

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, за исключением строковых данных, которые поступают от потребителя.

85 голосов
/ 30 декабря 2008

Методы фабрики следует рассматривать как альтернативу конструкторам - в основном, когда конструкторы недостаточно выразительны, т. Е.

class Foo{
  public Foo(bool withBar);
}

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

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

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

68 голосов
/ 12 мая 2012

Одна ситуация, в которой я лично нахожу отдельные классы Фабрики, чтобы иметь смысл, - это когда последний объект, который вы пытаетесь создать, зависит от нескольких других объектов. Например, в 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), и в то же время централизованный, что облегчает обслуживание. Существуют и другие причины, по которым использование отдельных фабрик может быть полезным (например, тестируемость), но я нахожу этот конкретный пример использования, чтобы лучше всего показать, как классы Фабрики могут быть полезны.

16 голосов
/ 16 июля 2013

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

Давайте определимся с "фабричным методом":

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

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

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();
    }
}
14 голосов
/ 16 сентября 2008

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

13 голосов
/ 26 мая 2015

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

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

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

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

UML от

enter image description here

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

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

Создатель: Объявляет фабричный метод

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

Постановка задачи: Создать Фабрику Игр, используя Фабричные Методы, которые определяют интерфейс игры.

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

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<String,Game> games = new HashMap<String,Game>();
    /*  
        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

В этом примере показан класс Factory с реализацией FactoryMethod.

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

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

  3. public Game getGame(String gameName) является FactoryMethod в IGameFactory классе

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

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

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

Factory:

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

FactoryMethod

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

Вариант использования:

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

5 голосов
/ 16 сентября 2008

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

4 голосов
/ 16 сентября 2008

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

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

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

3 голосов
/ 09 сентября 2015

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

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

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

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

...