Различия между абстрактным фабричным шаблоном и фабричным методом

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

Из того, что я читал, я вижу, что шаблон фабричного метода позволяет вам определить, как создать отдельный конкретный продукт, но скрыть реализацию от клиента, так как они увидят общий продукт. Мой первый вопрос об абстрактной фабрике. Его роль - позволить вам создавать семейства конкретных объектов (которые могут зависеть от того, какую конкретную фабрику вы используете), а не только один конкретный объект? Абстрактная фабрика возвращает только один очень большой объект или много объектов в зависимости от того, какие методы вы вызываете?

Мои последние два вопроса касаются одной цитаты, которую я не могу полностью понять, которую я видел во многих местах:

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

Насколько я понимаю, шаблон фабричного метода имеет интерфейс Creator, который позволит ConcreteCreator знать, какой экземпляр ConcreteProduct нужно создать. Это то, что это означает, используя наследование для обработки объекта?

Теперь, что касается этой цитаты, как именно шаблон Abstract Factory делегирует ответственность за создание экземпляра объекта другому объекту посредством композиции? Что это значит? Похоже, что шаблон Abstract Factory также использует наследование для выполнения процесса построения в моих глазах, но опять же я все еще изучаю эти шаблоны.

Любая помощь, особенно с последним вопросом, будет принята с благодарностью.

22 ответа

Решение

Разница между двумя

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

Поскольку фабричный метод является просто методом, он может быть переопределен в подклассе, отсюда и вторая половина вашей цитаты:

... шаблон фабричного метода использует наследование и опирается на подкласс для обработки желаемого экземпляра объекта.

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

Абстрактная фабрика - это объект, на котором есть несколько фабричных методов. Глядя на первую половину вашей цитаты:

... с шаблоном Abstract Factory класс делегирует ответственность за создание экземпляра объекта другому объекту посредством композиции...

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

Примеры кода

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

class A {
    public void doSomething() {
        Foo f = makeFoo();
        f.whatever();   
    }

    protected Foo makeFoo() {
        return new RegularFoo();
    }
}

class B extends A {
    protected Foo makeFoo() {
        //subclass is overriding the factory method 
        //to return something different
        return new SpecialFoo();
    }
}

И здесь используется абстрактная фабрика:

class A {
    private Factory factory;

    public A(Factory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        //The concrete class of "f" depends on the concrete class
        //of the factory passed into the constructor. If you provide a
        //different factory, you get a different Foo object.
        Foo f = factory.makeFoo();
        f.whatever();
    }
}

interface Factory {
    Foo makeFoo();
    Bar makeBar();
    Aycufcn makeAmbiguousYetCommonlyUsedFakeClassName();
}

//need to make concrete factories that implement the "Factory" interface here

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

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

Фабричный метод - это простой метод, используемый для создания объектов в классе. Обычно он добавляется в совокупный корень (Order класс имеет метод с именем CreateOrderLine)

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

Абстрактная фабрика

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

interface IMessageQueueFactory
{
  IMessageQueue CreateOutboundQueue(string name);
  IMessageQueue CreateReplyQueue(string name);
}

public class AzureServiceBusQueueFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new AzureMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new AzureResponseMessageQueue(/*....*/);
      }

}

public class MsmqFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new MsmqMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new MsmqResponseMessageQueue(/*....*/);
      }
}

Заводской метод

Проблема в HTTP-серверах заключается в том, что нам всегда нужен ответ на каждый запрос.

public interface IHttpRequest
{
    // .. all other methods ..

    IHttpResponse CreateResponse(int httpStatusCode);
}

Без фабричного метода пользователи HTTP-сервера (т. Е. Программисты) были бы вынуждены использовать классы, специфичные для реализации, которые противоречат цели IHttpRequest интерфейс.

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

Резюме

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

При использовании фабричных методов следует соблюдать осторожность, поскольку при создании объектов легко нарушить LSP ( принцип подстановки Лискова).

Различия между шаблонами проектирования AbstractFactory и Factory заключаются в следующем:

  • Фабричный метод используется только для создания одного продукта, но Абстрактная Фабрика предназначена для создания семейств связанных или зависимых продуктов.
  • ШаблонFactory Method предоставляет клиенту метод для создания объекта, тогда как в случае Abstract Factory они предоставляют семейство связанных объектов, которые могут состоять из этих методов Factory.
  • Фабричный метод скрывает построение отдельного объекта, в то время как абстрактный фабричный метод скрывает построение семейства связанных объектов. Абстрактные фабрики обычно реализуются с использованием (набора) фабричных методов.
  • ШаблонAbstractFactory использует композицию для делегирования ответственности за создание объекта другому классу, в то время как шаблон проектирования Factory использует наследование и опирается на производный класс или подкласс для создания объекта.
  • Идея шаблона Factory Method заключается в том, что он учитывает случай, когда клиент не знает, какие конкретные классы ему потребуется создать во время выполнения, но просто хочет получить класс, который будет выполнять работу, в то время как шаблон AbstractFactory является лучшим используется, когда вашей системе необходимо создать несколько семейств продуктов или вы хотите предоставить библиотеку продуктов без раскрытия деталей реализации.

Реализация шаблона фабричного метода: Фабричный метод UML

Реализация PatternFactory Pattern:

Абстрактная фабрика UML

Основное различие между Абстрактной Фабрикой и Фабричным Методом состоит в том, что Абстрактная Фабрика реализуется Композицией; но фабричный метод реализован по наследству.

Да, вы правильно прочитали: главное различие между этими двумя шаблонами - это старая композиция против дебатов о наследовании.

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

Абстрактная Фабрика

  1. Здесь важно понять, что абстрактная фабрика вводится в клиента. Вот почему мы говорим, что Абстрактная Фабрика реализуется Композицией. Часто инфраструктура внедрения зависимостей выполняет эту задачу; но рамки для DI не требуются.
  2. Вторым критическим моментом является то, что конкретные фабрики здесь не являются реализациями Factory Method! Пример кода для Factory Method показан ниже.
  3. И, наконец, третий момент, на который следует обратить внимание, - это взаимосвязь между продуктами: в этом случае очереди исходящих и ответных сообщений. Один бетонный завод производит очереди Azure, другой MSMQ. GoF называет это отношение продукта "семьей", и важно знать, что семья в данном случае не означает иерархию классов.
public class Client {
    private final AbstractFactory_MessageQueue factory;

    public Client(AbstractFactory_MessageQueue factory) {
        // The factory creates message queues either for Azure or MSMQ.
        // The client does not know which technology is used.
        this.factory = factory;
    }

    public void sendMessage() {
        //The client doesn't know whether the OutboundQueue is Azure or MSMQ.
        OutboundQueue out = factory.createProductA();
        out.sendMessage("Hello Abstract Factory!");
    }

    public String receiveMessage() {
        //The client doesn't know whether the ReplyQueue is Azure or MSMQ.
        ReplyQueue in = factory.createProductB();
        return in.receiveMessage();
    }
}

public interface AbstractFactory_MessageQueue {
    OutboundQueue createProductA();
    ReplyQueue createProductB();
}

public class ConcreteFactory_Azure implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new AzureMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new AzureResponseMessageQueue();
    }
}

public class ConcreteFactory_Msmq implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new MsmqMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new MsmqResponseMessageQueue();
    }
}

Фабричный метод

  1. Наиболее важным моментом здесь является то, что ConcreteCreator это клиент. Другими словами, клиент является подклассом, чей родитель определяет factoryMethod(), Вот почему мы говорим, что Factory Method реализуется по наследству.
  2. Второй важный момент - помнить, что шаблон фабричного метода - это не что иное, как специализация шаблона метода шаблона. Два шаблона имеют одинаковую структуру. Они отличаются только по назначению. Фабричный метод является креативным (он что-то строит), тогда как шаблонный метод является поведенческим (он что-то вычисляет).
  3. И, наконец, третий момент, на который следует обратить внимание: Creator (родительский) класс вызывает свой собственный factoryMethod(), Если мы удалим anOperation() из родительского класса, оставляя только один метод, он больше не является шаблоном Factory Method. Другими словами, фабричный метод не может быть реализован менее чем с двумя методами в родительском классе; и один должен ссылаться на другого.
public abstract class Creator {
    public void anOperation() {
        Product p = factoryMethod();
        p.whatever();
    }

    protected abstract Product factoryMethod();
}

public class ConcreteCreator extends Creator {
    @Override
    protected Product factoryMethod() {
        return new ConcreteProduct();
    }
}

Разный И Разные Фабричные Образцы

Имейте в виду, что, хотя GoF определяет два разных шаблона Factory, они не являются единственными существующими шаблонами Factory. Они даже не являются наиболее часто используемыми фабричными шаблонами. Известным третьим примером является шаблон статической фабрики Джоша Блоха из Effective Java. Книга Head First Design Patterns включает в себя еще одну модель, которую они называют Simple Factory.

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

Abstract Factory - это интерфейс для создания связанных продуктов, но Factory Method - это только один метод. Абстрактная фабрика может быть реализована несколькими фабричными методами.

Абстрактная фабрика UML

Рассмотрим этот пример для простоты понимания.

Что предоставляют телекоммуникационные компании? Например, широкополосная связь, телефонная линия и мобильная связь, и вас попросят создать приложение, чтобы предлагать свои продукты своим клиентам.

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

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

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

В этом случае BundleFactory абстрактная фабрика, BroadbandFactory, PhonelineFactory а также MobileFactory являются Factory, Более того, эти фабрики будут иметь фабричный метод для инициализации отдельных продуктов.

Посмотрите пример кода ниже:

public class BroadbandFactory : IFactory {
    public static Broadband CreateStandardInstance() {
        // broadband product creation logic goes here
    }
}

public class PhonelineFactory : IFactory {
    public static Phoneline CreateStandardInstance() {
        // phoneline product creation logic goes here
    }
}

public class MobileFactory : IFactory {
    public static Mobile CreateStandardInstance() {
        // mobile product creation logic goes here
    }
}

public class BundleFactory : IAbstractFactory {

    public static Bundle CreateBundle() {
        broadband = BroadbandFactory.CreateStandardInstance();
        phoneline = PhonelineFactory.CreateStandardInstance();
        mobile = MobileFactory.CreateStandardInstance();

        applySomeDiscountOrWhatever(broadband, phoneline, mobile);
    }

    private static void applySomeDiscountOrWhatever(Broadband bb, Phoneline pl, Mobile m) {
        // some logic here
    }
}

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

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

Диаграмма высокого уровня фабричной и абстрактной фабричной модели,

диаграмма

Для получения дополнительной информации о методе Factory, обратитесь к этой статье.

Для получения дополнительной информации о методе Abstract factory, обратитесь к этой статье.

Пример из реальной жизни. (Легко запомнить)

завод

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

Абстрактная Фабрика

Теперь рассмотрим тот же пример двери. Вы можете пойти к плотнику, или вы можете пойти в магазин пластиковых дверей или магазин ПВХ. Все они являются дверными фабриками. Исходя из ситуации, вы решаете, к какой фабрике вам нужно подходить. Это как Абстрактная Фабрика.

Я объяснил здесь как шаблон фабричного метода, так и абстрактный фабричный шаблон, начиная с того, что не используя их, объясняя проблемы, а затем решая проблемы, используя вышеуказанные шаблоны https://github.com/vikramnagineni/Design-Patterns/tree/master

Есть довольно много определений. По сути, три общих способа описания фабричного паттерна :

  1. Простая фабрика

Простой метод / класс создания объекта на основе условия.

  1. Заводской метод

Шаблон проектирования фабричного метода с использованием подклассов для обеспечения реализации.

  1. Абстрактная фабрика

Шаблон проектирования «Абстрактная фабрика» создает семейства связанных или зависимых объектов без указания их конкретных классов.

Ссылка ниже была очень полезной - Сравнение фабрик - refactoring.guru

  1. Мой первый вопрос об абстрактной фабрике. Его роль - позволить вам создавать семейства конкретных объектов (которые могут зависеть от того, какую конкретную фабрику вы используете), а не только один конкретный объект?

Да. Целью Абстрактной Фабрики является:

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


  1. Абстрактная фабрика возвращает только один очень большой объект или много объектов в зависимости от того, какие методы вы вызываете?

В идеале он должен возвращать один объект на метод, который вызывает клиент.

  1. Насколько я понимаю, шаблон фабричного метода имеет интерфейс Creator, который позволит ConcreteCreator знать, какой экземпляр ConcreteProduct нужно создать. Это то, что это означает, используя наследование для обработки объекта?

Да. Фабричный метод использует наследование.

  1. Абстрактный шаблон фабрики делегирует ответственность создания объекта другому объекту через композицию? Что это значит?

AbstractFactory определяет FactoryMethod, а ConcreteFactory отвечает за создание ConcreteProduct. Просто следуйте примеру кода в этой статье.

Вы можете найти более подробную информацию в соответствующих сообщениях SE:

В чем принципиальная разница между фабричными и абстрактными фабричными моделями?

Шаблоны проектирования: фабрика против фабрики, метод против абстрактной фабрики

      generation 1 <- generation 2 <- generation 3
//example
shape <- rectangle, oval <- rectangle impressionism, rectangle surrealism, oval impressionism, oval surrealism

Фабрика

Пример использования: создать экземпляр одного объекта из

Это Creational узор, который позволяет создавать generation 2в простом месте. Соответствует SRP и OCP - все изменения внесены в один класс.

      enum ShapeType {
    RECTANGLE,
    OVAL
}

class Shape {}

//Concrete Products
//generation 2
class Rectangle extends Shape {}
class Oval extends Shape {}

//Factory
class Factory {
    Shape createShape(ShapeType type) {

        switch (type) {
            case RECTANGLE:
                return new Rectangle();
            case OVAL:
                return new Oval();
        }
    }
}

//Creator
class Painter {

    private Factory factory;

    Painter(Factory factory) {
        this.factory = factory;
    }

    Shape prepareShape(ShapeType type) {
        return factory.createShape(type);
    }
}

//using
class Main {
    void main() {
        Painter painter = new Painter(new ShapeFactory());

        Shape shape1 = painter.prepareShape(ShapeType.RECTANGLE);
        Shape shape2 = painter.prepareShape(ShapeType.OVAL);
    }
}

Заводской метод

Пример использования: создать экземпляр одного объекта из

Помогает работать со следующим поколением членов семьи. У каждого художника свой стиль - импрессионизм, сюрреализм ... Factory Method использует абстрактные Creator как Factory (абстрактный метод) и Concrete Creators являются реализациями этого метода

      enum ShapeType {
    RECTANGLE,
    OVAL
}

class Shape {}

//Concrete Products
//generation 2
class Rectangle extends Shape {}
class Oval extends Shape {}

//generation 3
class RectangleImpressionism extends Rectangle {}
class OvalImpressionism extends Oval {}
class RectangleSurrealism extends Rectangle {}
class OvalSurrealism extends Oval {}

//Creator
abstract class Painter {

    Shape prepareShape(ShapeType type) {
        return createShape(type);
    }

    //Factory method
    abstract Shape createShape(ShapeType type);
}

//Concrete Creators
class PainterImpressionism {

    @override
    Shape createShape(ShapeType type) {
        switch (type) {
            case RECTANGLE:
                return new RectangleImpressionism();
            case OVAL:
                return new OvalImpressionism();
        }
    }
}

class PainterSurrealism {

    @override
    Shape createShape(ShapeType type) {
        switch (type) {
            case RECTANGLE:
                return new RectangleSurrealism();
            case OVAL:
                return new OvalSurrealism();
        }
    }
}

//using
class Main {
    void main() {
        Painter painterImpressionism = new PainterImpressionism();
        Shape shape1 = painterImpressionism.prepareShape(ShapeType.RECTANGLE);

        Painter painterSurrealism = new PainterSurrealism();
        Shape shape2 = painterSurrealism.prepareShape(ShapeType.RECTANGLE);
    }
}

Абстрактная фабрика

Пример использования: создать экземпляры всех объектов generation 3

является частью абстрактного Factory и реализации в Concrete Factories

      
//Concrete Products
//generation 2
class Rectangle extends Shape {}
class Oval extends Shape {}

//generation 3
class RectangleImpressionism extends Rectangle {}
class OvalImpressionism extends Oval {}
class RectangleSurrealism extends Rectangle {}
class OvalSurrealism extends Oval {}

//Abstract Factory
interface Factory {
    Rectangle createRectangle();
    Oval createOval();
}

//Concrete Factories
class ImpressionismFactory implements Factory {
    @Override
    public Rectangle createRectangle() {
        return new RectangleImpressionism();
    }

    @Override
    public Oval createOval() {
        return new OvalImpressionism();
    }
}

class SurrealismFactory implements Factory {
    @Override
    public Rectangle createRectangle() {
        return new RectangleSurrealism();
    }

    @Override
    public Oval createOval() {
        return new OvalSurrealism();
    }
}

//Creator
class Painter {

    Rectangle rectangle;
    Oval oval;

    Painter(Factory factory) {
        rectangle = factory.createRectangle();
        rectangle.resize();

        oval = factory.createOval();
        oval.resize();
    }
}

//using
class Main {
    void main() {
        Painter painter1 = new Painter(new ImpressionismFactory());
        Shape shape1 = painter1.rectangle;
        Shape shape2 = painter1.oval;

        Painter painter2 = new Painter(new ImpressionismFactory());
        Shape shape3 = painter2.rectangle;
        Shape shape4 = painter1.oval;
    }
}

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

Поймите различия в мотивациях:

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

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

Простым (заимствуя идею от GoF) являются любые приложения с графическим интерфейсом, скажем, виртуальный монитор, который имитирует внешний вид ОС MS, Mac или Fedora. Здесь, например, когда все объекты виджета, такие как окно, кнопка и т. Д., Имеют вариант MS, за исключением полосы прокрутки, которая получена из варианта MAC, назначение инструмента не удается.

Эти вышеупомянутые случаи формируют фундаментальную потребность в абстрактной фабричной модели.

С другой стороны, представьте, что вы пишете фреймворк, чтобы многие люди могли создавать различные инструменты (например, один из приведенных выше примеров), используя ваш фреймворк. По самой идее фреймворка вам не нужно, хотя вы не можете использовать конкретные объекты в своей логике. Вы скорее заключаете какие-то контракты высокого уровня между различными объектами и тем, как они взаимодействуют. Пока вы (как разработчик фреймворка) остаетесь на очень абстрактном уровне, каждый создатель инструмента вынужден следовать вашим фреймворк-конструкциям. Тем не менее, они (создатели инструментов) могут свободно выбирать, какой объект будет построен и как все объекты, которые они создают, будут взаимодействовать. В отличие от предыдущего случая (Pattern Abstract Factory Pattern), вам (как создателю фреймворка) в этом случае не нужно работать с конкретными объектами; и скорее может остаться на уровне контракта объектов. Кроме того, в отличие от второй части предыдущих мотиваций, у вас или у создателей инструментов никогда не бывает ситуаций смешивания объектов из вариантов. Здесь, пока код фреймворка остается на уровне контракта, каждый создатель инструмента ограничен (по природе самого случая) использованием своих собственных объектов. Создание объектов в этом случае делегируется каждому реализатору, а провайдеры инфраструктуры просто предоставляют единые методы для создания и возврата объектов. Такие методы неизбежны для разработчика инфраструктуры для продолжения работы со своим кодом и имеют специальное имя, называемое фабричным методом (Factory Method Pattern для базового шаблона).

Несколько заметок:

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

Образец кода:

//Part of framework-code
BoardGame {
    Board createBoard() //factory method. Default implementation can be provided as well
    Piece createPiece() //factory method

    startGame(){        //template method
         Board borad = createBoard()
         Piece piece = createPiece()
         initState(board, piece)
    }
}


//Part of Tool-builder code
Ludo inherits  BoardGame {
     Board createBoard(){ //overriding of factory method
         //Option A: return new LudoBoard() //Lodu knows object creation
         //Option B: return LudoFactory.createBoard() //Lodu asks AbstractFacory
     }
….
}

//Part of Tool-builder code
Chess inherits  BoardGame {
    Board createBoard(){ //overriding of factory method
        //return a Chess board
    }
    ….
}

На мой взгляд, ответ @TomDalling действительно правильный (чего бы он ни стоил), однако в комментариях все еще остается много путаницы.

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

Если вы новичок в шаблонах, эти примеры, вероятно, не лучшее место для начала.

Заводской метод

Client.javaish

      Client(Creator creator) {
    ProductA a = creator.createProductA();
}

Creator.javaish

      Creator() {}

void creatorStuff() {
    ProductA a = createProductA();
    a.doSomething();
    ProductB b = createProductB();
    b.doStuff();
}

abstract ProductA createProductA();

ProductB createProductB() {
    return new ProductB1();
}

Почему есть и?

Почему нет? В может использоваться с обоими, но это будет тип, который определяет конкретный созданный продукт.

Почему нет абстракция в ?

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

Я думал, что фабричные методы создают только один продукт?

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

Абстрактная фабрика

Client.javaish

      AbstractFactory factory;

Client() {
    if (MONDAY) {
        factory = new Factory2();
    } else {
        factory = new AbstractFactory();
    }
}

void clientStuff() {
    ProductA a = factory.createProductA();
    a.doSomething();
    ProductB b = factory.createProductB();
    b.doStuff();
}

Ждать! У тебя нет, ну ... эээ, абстракция

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

Святой дым, Бэтмен! не отменяет , что случилось с «семействами продуктов»?

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

Это не может быть правильным, не используется инъекция зависимостей

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

Путаница здесь в том, что люди путают композицию с внедрением зависимостей . HAS-A независимо от того, как он его получил. В отличие от отношений IS-A, а также не имеют наследства между ними.

Ключевые отличия

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

Резюме

Цель завода заключается в создании объектов, либо к клиенту или сам по себе.

У создателя есть свои обязанности, и ему может потребоваться использовать объекты или передать их клиенту.

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

Только абстрактная фабрика:

Предоставить интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов. - GoF


Код PlantUML, если вы хотите поиграть с диаграммами:

      @startuml FactoryMethod
abstract class Creator {
    creatorStuff()
    {abstract} createProductA(): ProductA
    createProductB(): ProductB
}
class Creator1 {
    createProductA(): ProductA
}
class Creator2 {
    createProductA(): ProductA
    createProductB(): ProductB
}

together {
    interface ProductA {
        doSomething()
    }
    class ProductA1
    ' class Product1B
}
together {
    interface ProductB {
        doStuff()
    }
    class ProductB1
    class ProductB2
}
Client --> Creator

Creator <|-- Creator1
Creator <|-- Creator2

Creator --> ProductB1
ProductA1 <-- Creator1
ProductA1 <-- Creator2
ProductB2 <-- Creator2

ProductA <|.. ProductA1
ProductB <|.. ProductB1
ProductB <|.. ProductB2

ProductA <- Creator

@enduml
      @startuml AbstractFactory

together {
    interface ProductA {
        doSomething()
    }
    class ProductA1
}

together {
    interface ProductB {
        doStuff()
    }
    class ProductB1
    class ProductB2
}

class AbstractFactory {
    createProductA(): ProductA
    createProductB(): ProductB
    --
    -
}

class Factory2 {
    createProductB(): ProductB
}

Client --> AbstractFactory
AbstractFactory <|-- Factory2

ProductA <|.. ProductA1
ProductB <|.. ProductB1
ProductB <|.. ProductB2

AbstractFactory --> ProductA1
AbstractFactory --> ProductB1
ProductB2 <-- Factory2

@enduml

Чтобы сделать это очень просто с минимальным интерфейсом и, пожалуйста, сфокусируйте "//1":

class FactoryProgram
    {
        static void Main()
        {
            object myType = Program.MyFactory("byte");
            Console.WriteLine(myType.GetType().Name);

            myType = Program.MyFactory("float"); //3
            Console.WriteLine(myType.GetType().Name);

            Console.ReadKey();
        }

        static object MyFactory(string typeName)
        {
            object desiredType = null; //1
            switch (typeName)
            {
                case "byte": desiredType = new System.Byte(); break; //2
                case "long": desiredType = new System.Int64(); break;
                case "float": desiredType = new System.Single(); break;
                default: throw new System.NotImplementedException();
            }
            return desiredType;
        }
    }

Здесь важные моменты: 1. Механизмы Factory & AbstractFactory должны использовать наследование (System.Object-> byte, float ...); так что если у вас есть наследование в программе, то Factory(абстрактный Factory, скорее всего, там не было бы) уже существует по проекту 2. Creator (MyFactory) знает о конкретном типе, поэтому возвращает объект конкретного типа вызывающей стороне (Main); В абстрактной фабрике возвращаемым типом будет интерфейс.

interface IVehicle { string VehicleName { get; set; } }
interface IVehicleFactory
    {
        IVehicle CreateSingleVehicle(string vehicleType);
    }
class HondaFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports": return new SportsBike();
                case "Regular":return new RegularBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }
class HeroFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports":  return new SportsBike();
                case "Scooty": return new Scooty();
                case "DarkHorse":return new DarkHorseBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }

class RegularBike : IVehicle { public string VehicleName { get { return "Regular Bike- Name"; } set { VehicleName = value; } } }
class SportsBike : IVehicle { public string VehicleName { get { return "Sports Bike- Name"; } set { VehicleName = value; } } }
class RegularScooter : IVehicle { public string VehicleName { get { return "Regular Scooter- Name"; } set { VehicleName = value; } } }
class Scooty : IVehicle { public string VehicleName { get { return "Scooty- Name"; } set { VehicleName = value; } } }
class DarkHorseBike : IVehicle { public string VehicleName { get { return "DarkHorse Bike- Name"; } set { VehicleName = value; } } }

class Program
{
    static void Main(string[] args)
    {
        IVehicleFactory honda = new HondaFactory(); //1
        RegularBike hondaRegularBike = (RegularBike)honda.CreateSingleVehicle("Regular"); //2
        SportsBike hondaSportsBike = (SportsBike)honda.CreateSingleVehicle("Sports");
        Console.WriteLine("******* Honda **********"+hondaRegularBike.VehicleName+ hondaSportsBike.VehicleName);

        IVehicleFactory hero = new HeroFactory();
        DarkHorseBike heroDarkHorseBike = (DarkHorseBike)hero.CreateSingleVehicle("DarkHorse");
        SportsBike heroSportsBike = (SportsBike)hero.CreateSingleVehicle("Sports");
        Scooty heroScooty = (Scooty)hero.CreateSingleVehicle("Scooty");
        Console.WriteLine("******* Hero **********"+heroDarkHorseBike.VehicleName + heroScooty.VehicleName+ heroSportsBike.VehicleName);

        Console.ReadKey();
    }
}

Важные моменты: 1. Требование: Honda создаст "Regular", "Sports", а Hero создаст "DarkHorse", "Sports" и "Scooty". 2. почему два интерфейса? Один для типа производителя (IVehicleFactory) и другой для продукта фабрики (IVehicle); Другой способ понять 2 интерфейса - абстрактная фабрика - все о создании связанных объектов 2. Подвох - возвращение потомков IVehicleFactory и IVehicle(вместо бетона на фабрике); поэтому я получаю родительскую переменную (IVehicle); затем я создаю конкретный конкретный тип, вызывая CreateSingleVehicle и затем приводя родительский объект к фактическому дочернему объекту. Что будет, если я сделаю RegularBike heroRegularBike = (RegularBike)hero.CreateSingleVehicle("Regular");; вы получите ApplicationException, и поэтому нам нужна общая абстрактная фабрика, которую я объясню, если потребуется. Надеюсь, это поможет от начинающих до промежуточной аудитории.

А) Шаблон фабричного метода

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

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

Б) Шаблон абстрактной фабрики

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

Что такое «семейства объектов»? Например, возьмем этот набор классов: Транспорт + Двигатель + Управление. Их может быть несколько вариантов:

1- Автомобиль + Двигатель сгорания + Рулевое колесо

2- Самолет + JetEngine + Хомут

Если ваша программа не работает с семействами продуктов, вам не нужна абстрактная фабрика.

И снова, многие люди путают абстрактный фабричный паттерн с простым фабричным классом, объявленным как абстрактный. Не делай этого!

ССЫЛКА: https://refactoring.guru/design-patterns/factory-comparison

Абстрактная фабрика: фабрика заводов; фабрика, которая группирует отдельные, но связанные / зависимые фабрики вместе без указания их конкретных классов. Пример абстрактной фабрики

Фабрика: предоставляет способ делегировать логику реализации дочерним классам. Пример фабричного образца

Я бы предпочел абстрактную фабрику над фабричной методикой в ​​любое время. Из приведенного выше примера Тома Даллинга (отличное объяснение) мы можем видеть, что Абстрактная Фабрика является более компонуемой в том смысле, что все, что нам нужно сделать, это передать другую Фабрику конструктору (здесь используется внедрение зависимости конструктора). Но Factory Method требует от нас ввести новый класс (больше вещей для управления) и использовать подклассы. Всегда предпочитайте композицию наследству.

Многие из приведенных выше ответов не обеспечивают сравнения кода между шаблоном Abstract Factory и Factory Method. Следующее - моя попытка объяснить это через Java. Надеюсь, это поможет кому-то нуждающемуся в простом объяснении.

Как метко говорит GoF: Abstract Factory предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.

        public class Client {
            public static void main(String[] args) {
               ZooFactory zooFactory = new HerbivoreZooFactory();       
               Animal animal1 = zooFactory.animal1();
               Animal animal2 = zooFactory.animal2();
               animal1.sound();
               animal2.sound();

               System.out.println();

               AnimalFactory animalFactory = new CowAnimalFactory();
               Animal animal = animalFactory.createAnimal();
               animal.sound();
            }
        }

        public interface Animal {
            public void sound();
        }

        public class Cow implements Animal {

            @Override
            public void sound() {
                System.out.println("Cow moos");
            }

        }

        public class Deer implements Animal {

            @Override
            public void sound() {
                System.out.println("Deer grunts");
            }

        }

        public class Hyena implements Animal {

            @Override
            public void sound() {
                System.out.println("Hyena.java");
            }

        }

        public class Lion implements Animal {

            @Override
            public void sound() {
                System.out.println("Lion roars");
            }

        }

        public interface ZooFactory {
            Animal animal1();

            Animal animal2();
        }

        public class CarnivoreZooFactory implements ZooFactory {

            @Override
            public Animal animal1() {
                return new Lion();
            }

            @Override
            public Animal animal2() {
                return new Hyena();
            }

        }

        public class HerbivoreZooFactory implements ZooFactory{

            @Override
            public Animal animal1() {
                return new Cow();
            }

            @Override
            public Animal animal2() {
                return new Deer();
            }

        }

        public interface AnimalFactory {
            public Animal createAnimal();
        }

        public class CowAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Cow();
            }

        }

        public class DeerAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Deer();
            }

        }

        public class HyenaAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Hyena();
            }

        }

        public class LionAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Lion();
            }

        }

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

Абстрактный шаблон проектирования фабрики на примере в реальном времени: что такое абстрактный шаблон проектирования фабрики? Он похож на шаблон проектирования фабричного метода. нам нужно использовать этот шаблон, когда у нас несколько фабрик. в этом шаблоне будет определена группа предприятий. Шаблон фабричного метода - это подмножество абстрактного фабричного шаблона проектирования. Они имеют те же преимущества, что и заводские выкройки. абстрактная фабрика полагается на композицию объектов, тогда как фабричный метод имеет дело с наследованием. шаблон проектирования фабрики на java с примером в реальном времени: что такое шаблон проектирования фабрики? это в основном используется в объектно-ориентированном программировании. Это один из творческих паттернов. все дело в создании экземпляров. Клиенты будут создавать объект, не подвергаясь воздействию логики создания объекта. он широко используется в различных каркасах, например, в пружинных каркасах.мы используем этот шаблон, когда класс не знает объекты другого, которые он должен создать. Пример в реальном времени: когда наша машина ломается на дороге. Нам необходимо сообщить мастеру по ремонту, какой тип автомобиля мы используем, чтобы мастер имел инструменты для ремонта. Согласно нашим данным, мастер по ремонту исправит проблему и подготовит нас к новой поездке. Есть несколько встроенных методов, использующих эти шаблоны. пример метода getInstance() в классе JavaUtilcalendar. С помощью getInstance() мы можем получать объекты всякий раз, когда выполняем этот метод. Javautilcalendar: getInstance() - это объект, возвращаемый методом.Нам необходимо сообщить мастеру по ремонту, какой тип автомобиля мы используем, чтобы мастер имел инструменты для ремонта. Согласно нашим данным, мастер по ремонту исправит проблему и подготовит нас к новой поездке. Есть несколько встроенных методов, использующих эти шаблоны. пример метода getInstance() в классе JavaUtilcalendar. С помощью getInstance() мы можем получать объекты всякий раз, когда выполняем этот метод. Javautilcalendar: getInstance() - это объект, возвращаемый методом.Нам необходимо сообщить мастеру по ремонту, какой тип автомобиля мы используем, чтобы мастер имел инструменты для ремонта. Согласно нашим данным, мастер по ремонту исправит проблему и подготовит нас к новой поездке. Есть несколько встроенных методов, использующих эти шаблоны. пример метода getInstance() в классе JavaUtilcalendar. С помощью getInstance() мы можем получать объекты всякий раз, когда выполняем этот метод. Javautilcalendar: getInstance() - это объект, возвращаемый методом. https://trendydevx.com/factory-design-pattern-in-java-with-realtime-example/

Мой вывод: разницы нет. Почему? Потому что я не вижу никакого оправдания для оснащения объектов, кроме заводов, фабричным способом - иначе вы получите нарушение принципа разделения ответственности. Кроме того, я не вижу никакой разницы между фабрикой с одним фабричным методом и фабрикой с несколькими фабричными методами: обе создают «семейства связанных объектов», если только кто-нибудь не сможет доказать, что семья из одного члена семьи не является семьей. Или коллекция, содержащая один элемент, не является коллекцией.

Другие вопросы по тегам