Шаблони дизайну: Анотація Фабрика проти заводський метод


141

Примітка. Питання - в кінці публікації.

Я прочитав інші потоки stackoverflow щодо методу абстрактного заводу проти фабрики . Я розумію наміри кожного зразків. Однак я не чіткий щодо визначення.

Фабричний метод визначає інтерфейс для створення об'єкта, але дозволяє підкласам вирішувати, який із них використовувати. Фабричний метод дозволяє класам відкладати екземпляри до підкласів.

Навпаки, абстрактний завод пропонує інтерфейс для створення сімей пов’язаних або залежних об'єктів без конкретизації їх конкретних класів.

- Джон Фемінелла

Abstract Factory виглядає дуже схожий на метод фабрики . Я намалював кілька класів UML, щоб проілюструвати свою суть.

Примітка:

  • Діаграма з www.yuml.com, тому вони не ідеально орієнтовані. Але це безкоштовний сервіс :).
  • Діаграми можуть бути не ідеальними. Я досі вивчаю шаблони дизайну GoF .

Заводський метод:

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

Абстрактна фабрика (лише 1 член):

Абстрактна фабрика (лише 1 член)

Анотація заводу (більше членів):

alt текст

Запитання:

  1. Якщо в « Фабриці абстрактних» є лише один творець і один продукт, чи це все ще шаблон « Фабрика абстрактних»? (інтерфейс для створення сімей)
  2. Чи можна створити бетонний засіб " Фабричний метод" з інтерфейсу чи це має бути з класу? (класи відкладають екземпляри до підкласів)
  3. Якщо абстрактна фабрика може мати лише одного творця та один продукт, чи є єдиною різницею між « Фабрикою абстрактних» та « Фабричним методом» те, що творець для першого - «Інтерфейс», а «другий» - «Клас»?

1
Примітка: Коли я посилаюся на інтерфейс, я більше думав з точки зору інтерфейсу Java (абстрактний клас з абстрактними віртуальними методами). Не соромтеся уточнити, чи існує різниця між абстрактним заводу та фабричним методом різними мовами.

Основна різниця тут: stackoverflow.com/questions/1001767 , хоча не настільки конкретна, як ви просите ..
nawfal

Відповіді:


134

Сподіваюся, це допомагає. У ньому описані різні типи заводів. Я використовував шаблони дизайну Head First Design . Я використовував yuml.me для діаграми.

Статична фабрика

Це клас зі статичним методом для виготовлення різних підтипів продукту.

Статична фабрика

Проста фабрика

Це клас, який може виробляти різні підтипи продукту. (Це краще, ніж статична фабрика. Коли додаються нові типи, базовий клас продукту не потрібно змінювати лише простий заводський клас)

Простий Фабрит

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

Містить один метод отримання одного виду виробу, пов'язаного з його типом. (Це краще, ніж простий завод, тому що тип віднесений до підкласу.)

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

Анотація заводу

Виробляє сімейство типів, які пов'язані між собою. Він помітно відрізняється від заводського методу, оскільки він має більше одного способу типів, які він виробляє. (Це складно, див. Наступну схему для кращого прикладу реального життя).

Анотація заводу

Приклад із .NET Framework

DbFactoriesProvider - це проста фабрика, оскільки вона не має підтипів. DbFactoryProvider - це абстрактна фабрика, оскільки вона може створювати різні пов'язані об'єкти бази даних, такі як об'єкти з'єднання та команди.

Анотація заводу від .NET Framework Сігналы абмеркавання


Чи різниця між Static Factory і Simple Factory полягає лише в тому, що метод CreateProduct знаходиться в іншому класі?
Peter O'Callaghan

4
Хіба не було б зрозуміліше, якби у випадку Фабричного методу існували просто Product(як абстрактні), а потім Product1і Product2як сини? Це допомогло б сказати, що заводський метод полягає лише у створенні одного продукту, тоді як абстрактна фабрика - це більш-менш купа Фабричного методу, зібраного в сім'ї.
lllllll

79

Два зразки, безумовно, пов'язані!

Різниця між моделями, як правило, у намірах.

Намір з Factory Method є «Визначити інтерфейс для створення об'єкта, але нехай підкласи вирішити , який клас інстанціювати. Фабричний метод дозволяє в клас відкласти створення екземпляра на підкласи.»

Намір в Abstract Factory є «Забезпечення інтерфейсу для створення сімейств пов'язаних або залежних об'єктів без зазначення їх конкретних класів.»

Грунтуючись виключно на цих заявах про наміри (цитується з GoF), я б сказав, що насправді Фабричний метод у певному сенсі є "виродженим" абстрактним заводом із сімейством одного.

Вони, як правило, відрізняються в реалізації, оскільки Фабричний метод набагато простіший, ніж абстрактний завод .

Однак вони пов'язані також і при впровадженні. Як зазначається в книзі GoF,

AbstractFactory оголошує лише інтерфейс для створення продуктів. Реально їх створити підкласи ConcreteProduct. Найпоширеніший спосіб зробити це - визначити заводський метод для кожного продукту.

У цій вікі c2 також є цікава дискусія на цю тему.


7
Я не розумію ні коментаря, ні протидії. Чи можете ви докладно?
Дон Робі

Що ж, відповіді мені здаються риторичними ... Немає справжнього конкретного прикладу ... надто широкого ...
Новаліс,

14

Схоже, перелік (відмінних) питань ОП проігноровано. Поточні відповіді лише пропонують переосмислені визначення. Тож я спробую вирішити оригінальні питання стисло.

  1. Якщо в « Фабриці абстрактних» є лише один творець і один продукт, чи це все ще шаблон « Фабрика абстрактних»? (інтерфейс для створення сімей)

Ні . Абстрактна фабрика повинна створити більше одного продукту, щоб створити "сімейство споріднених продуктів". Приклад канонічного GoF створює ScrollBar()і Window(). Перевага (і призначення) полягає в тому, що абстрактна фабрика може застосовувати загальну тему у своїх кількох продуктах.

  1. Чи можна створити бетонний засіб " Фабричний метод" з інтерфейсу чи це має бути з класу? (класи відкладають екземпляри до підкласів)

По-перше, ми маємо зазначити, що ні Java, ні C # не існували, коли GoF писав свою книгу. Використання терміну інтерфейс GoF не пов'язане з типами інтерфейсів, введених певними мовами. Тому конкретного творця можна створити з будь-якого API. Важливим моментом у шаблоні є те, що API споживає власний заводський метод, тож інтерфейс лише з одним методом не може бути більш заводським методом, ніж може бути абстрактним заводу.

  1. Якщо абстрактна фабрика може мати лише одного творця та один продукт, чи є єдиною відмінністю між « Фабрикою абстрактних» та « Фабричним методом» те, що творцем для першого є Інтерфейс, а творцем для останнього - Клас?

Це запитання вже не є дійсним, якщо відповідати вище. однак, якщо ви не думаєте, що єдиною різницею між абстрактним заводським та заводським методом є кількість створених продуктів, подумайте, як клієнт споживає кожну з цих моделей. Абстрактна фабрика, як правило, вводиться своєму клієнту і викликається за допомогою складу / делегації. Фабричний метод повинен бути успадкований. Отже, все повертається до старої композиції проти дебатів про спадщину.

Але ці відповіді викликали четверте запитання!

  1. Оскільки інтерфейс лише з одним методом не може бути Фабричним методом більше, ніж це може бути Абстрактна Фабрика , що ми називаємо творчим інтерфейсом лише одним методом?

Якщо метод є статичним, його зазвичай називають статичною фабрикою . Якщо метод нестатичний, його зазвичай називають Simple Factory . Жоден із них не є зразком GoF, але на практиці вони набагато частіше використовуються!


1
Щодо композиції проти успадкування, я завжди цікавився: чи не можливо також скласти композицію за допомогою фабричного методу? Що б завадило одному скласти або ввести потрібний бетонний завод у клієнта? Або це вже щось поза рамками шаблону?
georaldc

1
@georaldc, з GoF (стор. 107) " Фабричний метод дозволяє класу відкласти екземпляри підкласам. " Іншими словами, Factory Method використовує успадкування за визначенням.
jaco0646

4

На мій погляд, незначна різниця між двома закономірностями полягає у застосуванні , і так, як уже було сказано, у Намір .

Давайте резюмуємо визначення (обидва з Вікіпедії).

Анотація заводу

Забезпечте інтерфейс для створення сімей пов’язаних або залежних об'єктів, не вказуючи їх конкретні класи.

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

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

Обидва шаблони дозволяють від'єднати об'єкти користувача від створення необхідних екземплярів (розв'язка часу виконання), і це загальний аспект. Обидві моделі дозволяють створити ієрархію заводів відповідно до будь-яких конкретних потреб, і це ще один загальний аспект.

Abstract Factory дозволяє створити декілька різних типів екземплярів в одному підкласі та деталізувати поведінку творінь у різних його підкласах; як правило, заводський метод оголошує створення лише одного типу об’єктів, який може бути деталізований відповідно до механізму підкласифікації. Ось і різниця.

Підсумовуючи. Скажімо, що Product визначає суперклас створюваних об'єктів, і що ProductA і ProductB є двома різними підкласами. Таким чином, метод абстрактного заводу матиме два методи: createProductA () та createProductB (), які будуть деталізовані (з точки зору кроків створення) у своїх специфічних підкласах: заводські підкласи деталізують етапи створення для двох визначених класів об'єктів, що створюються.

Згідно з вищенаведеним прикладом, Фабричний метод буде реалізовуватися по-різному, абстрагуючи створення ProductA і ProductB на багатьох заводах (один метод на Фабрику), а подальша спеціалізація етапів створення буде делегована ієрархії під час її побудови. .


2

Якби я створив абстракцію (на яку посилається через інтерфейс або абстрактний базовий клас) Factory Class, який створює об'єкти, у яких є лише один метод створення об’єктів, то це був би заводський метод .

Якщо в абстрагованій Фабриці було більше 1 методу створення об'єктів, то це була б абстрактна фабрика .

Скажімо, я створю менеджер, який буде відповідати потребам методів дій для контролера MVC. Якби у нього був один метод, скажімо, для створення об'єктів двигуна, які будуть використовуватися для створення моделей перегляду, то це був би заводський метод. З іншого боку, якби у нього було два способи: один для створення двигунів перегляду моделей та інший для створення двигунів моделей дій (або як би ви хотіли назвати модель, що метод дії містить споживачів), то це була б абстрактна фабрика.

public ActionResult DoSomething(SpecificActionModel model)
{
    var actionModelEngine = manager.GetActionModelEngine<SpecificActionModel>();
    actionModelEngine.Execute(SpecificActionModelEnum.Value);

    var viewModelEngine = manager.GetViewModelEngine<SpecificViewModel>();
    return View(viewModelEngine.GetViewModel(SpecificViewModelEnum.Value);
}

1

Хоча минуло багато років з тих пір, як люди зі StackOverflow допитувались щодо цього питання аналогічно в інших публікаціях (найстаріший - у 2009 році), я все ж не зміг знайти відповіді, яку хотів.


Тож я провів кілька годин дослідження через Інтернет, переглянувши приклади, і прийшов до цього висновку, основними відмінностями абстрактного заводу від фабричного методу є:

  • Намір: узгодженість або «вигляд-відчуття» : Намір абстрактної фабрики полягає в групуванні родини об’єктів з однаковим стилем (наприклад, віджети інтерфейсу інтерфейсу типу «вигляд і відчуття», деталі автомобілів у одному стилі, об'єкти з однієї ОС, та ін.) Багато прикладів з абстрактної фабрики згадує про ключову фразу "той самий вигляд і відчуття".
  • Об'єкти, що утворюють об'єкт більшої групи : абстрактний завод створює сімейство об'єктів, що утворюють об'єкт більшої групи, а не один об'єкт.
  • Пізніше додати новий стиль : Якщо ми продовжували використовуючи фабричний метод і спробувати додати новий набір стилю для існуючої інфраструктури, було б болючим. Завдяки абстрактній фабриці нам залишається просто створити нову фабрику з бетону, яка реалізує абстрактний заводський клас.

Приклад зустрічних був би

  • Автомобільна частина для спортивного автомобіля, що використовується в седані. Ця невідповідність може призвести до нещасних випадків.
  • Кнопка в стилі Windows у різних віджетах графічного інтерфейсу ОС. Це не порушить нічого, але зашкодить користувацькому досвіду для деяких людей, як я.
  • Пізніше ми з’ясуємо, що наше програмне забезпечення потрібно запустити в наступному оновлення ОС, що потребує різного набору сумісних системних об'єктів, зберігаючи програмне забезпечення назад сумісним.

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


0

Наскільки я розумію значення o Абстрактні визначення фабричних і заводських методів, перший реалізується в статичному контексті і забезпечує об'єкт на основі вхідних параметрів.

У другому використовується вже створений об'єкт (сімейство), який реалізує інтерфейс заводського методу. Потім заводський метод створює конкретний екземпляр, пов'язаний з оригінальним об'єктом, незалежно від того, який це.

Таким чином, це зазвичай призводить до використання обох шаблонів разом, коли на першому кроці ви створюєте якийсь загальний об'єкт, який описує сімейство споріднених об'єктів. Він викликається статичним методом getInstance ("моє прізвище"). Реалізація такого методу getInstance визначає, який сімейний об’єкт буде створений.

Тоді я викликаю метод createProduct () для новоствореного об'єкта сім'ї, і залежно від об'єкта сім'ї новий продукт буде повернутий.

Схоже, ці закономірності співпрацюють між собою.

Іншими словами, абстрактна фабрика орієнтована на "ЩО" буде створено, а заводський метод "ЯК" буде створений.


0

Все, що вам потрібно пам’ятати, це те, що абстрактна фабрика - це фабрика, яка може повернути кілька заводів . Тож якщо у вас був AnimalSpeciesFactory, він може повернути такі фабрики:

Ссавці, Жировики, Рибки, Фабрика. Тепер, коли у вас є одна фабрика з AnimalSpeciesFactory, вони використовують заводський зразок для створення конкретних об'єктів. Наприклад, уявіть, що ви отримали ReptileFactory від цієї AnimalFactory, тоді ви можете запропонувати створити об'єкти рептилій, такі як: Змії, черепахи, ящірки.


0
/*
//Factory methods:

//1. Factory Method - Abstract Creator Class



#include <iostream>
#include <string.h>
using namespace std;

const std::string nineNintyCC = std::string("990CC");
const std::string thousandTwoHundredCC = std::string("1200CC");
const std::string ThousandFiveHundredCC = std::string("1500CC");
const std::string fiveThousandCC = std::string("5000CC");

// Product
class Engine
{
    public:
    virtual void packEngine() = 0;  
};

// Concrete products
// concrete product class one
class C990CCEngine: public Engine
{

    public:
    void packEngine()
    {
       cout << "Pack 990CC engine" << endl;   
    }
};

// concrete class Two
class C1200CCEngine: public Engine
{   public:
    void packEngine()
    {
        cout << "pack 1200CC engine" << endl;
    }

};

// Concrete class Three
class C1500CCEngine: public Engine
{
    public:
    void packEngine()
    {
        cout << "Pack 1500CC engine" << endl;
    }

};


// Car Factory:
class CarFactory{
    public:

    virtual Engine* createEngine(const std::string& type) = 0;
};
class Factory: public CarFactory
{
    public:
     Engine *createEngine(const std::string& type)
     {

          if(0 == nineNintyCC.compare(type))
          {    
             return new C990CCEngine;
          }
          else if(0 == thousandTwoHundredCC.compare(type))
          {
             return new C1200CCEngine;
          }
          else if(0 == ThousandFiveHundredCC.compare(type))
          {
             return new C1500CCEngine;
          } 
          else
           {
                 cout << "Invalid factory input" << endl;
             return NULL;
           }
           return NULL;
     }
};

int main()
{

    CarFactory* ptr = new Factory;
    Engine*pEngine =  ptr->createEngine(nineNintyCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine =  ptr->createEngine(ThousandFiveHundredCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine =  ptr->createEngine(thousandTwoHundredCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    pEngine = ptr-> createEngine(fiveThousandCC);
    if(pEngine)
    {
        pEngine->packEngine();
        delete pEngine;
    }
    else
    {
        cout << "No engine exists of your type in our factory" << endl;
    }
    return 0;
}

*/
/*
//
// interface product
#include <iostream>
#include <string>
using namespace std;

class Engine
{
 public:
 virtual void EngineType() = 0;

};

// concrte product
class AltoEngine: public Engine
{
  public:
  void EngineType()
  {
      cout << "Alto Engine" << endl;
  }
};

//Concrte product
class SwiftEngine : public Engine
{
    public:
    void EngineType()
    {
        cout << "Swift Engine" << endl;    
    }
};

class Body
{
   public:
    virtual void bodyType() = 0;

};

class AltoBody: public Body
{
  public:  
    virtual void bodyType()
    {
        cout << "Alto Car Body" << endl;
    }
};

class SwiftBody : public Body
{
    public:
    void bodyType()
    {
        cout << "SwiftCar Body" << endl;
    }

};


class CarFactory
{
   public:
   virtual Engine* createEngineProduct() = 0;
   virtual Body*   createBodyPoduct() = 0;
};
class AltoCarFactory: public CarFactory
{
    public:
    Engine * createEngineProduct()
    {
        return new AltoEngine;
    }
    Body* createBodyPoduct()
    {
        return new AltoBody;
    }

};

class SwiftCarFactory: public CarFactory
{
    public:
    Engine * createEngineProduct()
    {
        return new SwiftEngine;
    }
    Body* createBodyPoduct()
    {
        return new SwiftBody;
    }

};

int main()
{

    CarFactory* pAltoFactory = new AltoCarFactory;
    Engine* pAltoEngine = pAltoFactory->createEngineProduct();
    pAltoEngine->EngineType();
    Body* pAltoBody = pAltoFactory->createBodyPoduct();
    pAltoBody->bodyType();



    CarFactory* pSwiftFactory = NULL;
    pSwiftFactory = new SwiftCarFactory;
    Engine* pSwiftEngine = pSwiftFactory->createEngineProduct();
    pSwiftEngine->EngineType();
    Body* pSwfitBody = pSwiftFactory->createBodyPoduct();
    pSwfitBody->bodyType();
    delete pAltoBody;
    delete pAltoFactory;
    delete pSwfitBody;
    delete pSwiftFactory;
    return 0;
}
*/

/*

// One more Factory example;

#include <iostream>
#include <string>
using namespace std;

const std::string maruthi = std::string("Maruthi");
const std::string fiat = std::string("Fiat");
const std::string renault = std::string("Renault");
// Interface
class CarEngine
{
 public:
    virtual void engineType() = 0;
};

// Concrete class
class FiatEngine: public CarEngine
{
  public:
  void engineType()
  {
      cout << "Fait Engine Engine" << endl;
  }

};
// ConcreteClass
class RenaultEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Renault Engine" << endl;
    }

};
// Concrete class
class MaruthiEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Maruthi Engine" << endl;
    }
};


// Factory
class CarFactory
{
    public:
    virtual CarEngine* createFactory(const std::string&) = 0;
};

// EngineFactory
class CarEngineFactory : public CarFactory
{
     public:
     CarEngine* createFactory(const std::string&  type)
     {
          if(0 == maruthi.compare(type))
          {
              return new MaruthiEngine;

          }
          else if(0 == fiat.compare(type))
          {
              return  new FiatEngine;
          }
          else if(0 == renault.compare(type))
          {
              return new RenaultEngine;
          }
          else
          {
              cout << "Invalid Engine type" << endl;
              return NULL;
          }
     }

  };

int main()
{
    CarFactory* pCarFactory = new CarEngineFactory;
    CarEngine* pMaruthiCarEngine = pCarFactory->createFactory(maruthi);
    pMaruthiCarEngine->engineType();

    CarEngine* pFiatCarEngine = pCarFactory->createFactory(fiat);
    pFiatCarEngine->engineType();


    CarEngine* pRenaultCarEngine = pCarFactory->createFactory(renault);
    pRenaultCarEngine->engineType();

    return 0;
}


*/


/*

// One more Factory example;

#include <iostream>
#include <string>
using namespace std;

const std::string maruthi = std::string("Maruthi");
const std::string fiat = std::string("Fiat");
const std::string renault = std::string("Renault");


// Interface
class CarEngine
{
 public:
    virtual void engineType() = 0;
};

// Concrete class
class FiatEngine: public CarEngine
{
  public:
  void engineType()
  {
      cout << "Fait Car Engine" << endl;
  }

};

// ConcreteClass
class RenaultEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Renault Car Engine" << endl;
    }

};

// Concrete class
class MaruthiEngine : public CarEngine
{
    public:
    void engineType()
    {
        cout << "Maruthi Car Engine" << endl;
    }
};

// Interface
class CarBody
{
 public:
    virtual void bodyType() = 0;
};

// Concrete class
class FiatBody: public CarBody
{
  public:
  void bodyType()
  {
      cout << "Fait car Body" << endl;
  }

};

// ConcreteClass
class RenaultBody : public CarBody
{
    public:
    void bodyType()
    {
        cout << "Renault Body" << endl;
    }

};

// Concrete class
class MaruthiBody : public CarBody
{
    public:
    void bodyType()
    {
        cout << "Maruthi body" << endl;
    }
};


// Factory
class CarFactory
{
    public:
    virtual CarEngine* createCarEngineProduct() = 0;
    virtual CarBody* createCarBodyProduct() = 0;
};

// FiatFactory
class FaitCarFactory : public CarFactory
{
     public:
     CarEngine* createCarEngineProduct()
     {
        return new FiatEngine; 
     }
     CarBody* createCarBodyProduct()
     {
         return new FiatBody;
     }
};

// Maruthi Factory
class MaruthiCarFactory : public CarFactory
{
     public:
     CarEngine* createCarEngineProduct()
     {
         return new MaruthiEngine;
     }
     CarBody* createCarBodyProduct()
     {
         return new MaruthiBody;
     }

};

// Renault Factory
class RenaultCarFactory : public CarFactory
{
     public:
    CarEngine* createCarEngineProduct()
    {
        return new RenaultEngine;
    }

    CarBody* createCarBodyProduct()
    {
        return new RenaultBody;
    }

};


int main()
{

   // Fiat Factory
   CarFactory* pFiatCarFactory = new FaitCarFactory;
   CarEngine* pFiatEngine = pFiatCarFactory->createCarEngineProduct();
   CarBody*  pFiatBody = pFiatCarFactory->createCarBodyProduct();
   pFiatEngine->engineType();
   pFiatBody->bodyType();

   // Renault Car Factory
    return 0;
}

*/

-1

Заводський метод візерунка - це модель креативного дизайну, яка займається створенням об'єктів, не показуючи точний клас об'єктів, що створюється. Ця схема дизайну в основному дозволяє класу відкладати інстанціювання до підкласів.

Анотація «Фабрика фабрики» служить інкапсуляцією для групи окремих заводів, не піддаючи конкретних класів. У цій моделі загальний інтерфейс абстрактного фабричного класу використовується для створення необхідного конкретного об'єкта, що відокремлює деталі реалізації об'єктів від їх використання та складу. Цей шаблон дизайну широко застосовується в додатках GUI, де потрібно створити подібний тип графічних компонентів.

під час пошуку в Google я придумав наступний блог, який блискуче пояснив обидві моделі дизайну. подивіться на це

http://simpletechtalks.com/factory-design-pattern/

http://simpletechtalks.com/abrief-factory-design-pattern/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.