Коли корисно використовувати фабричні методи в об’єкті замість класу Factory?
Коли корисно використовувати фабричні методи в об’єкті замість класу Factory?
Відповіді:
Мені подобається думати про дизайнерські паттенти з точки зору того, що мої заняття є "людьми".
Отже, для мене заводський зразок - це як агентство найму. У вас є хтось, кому знадобиться змінна кількість працівників. Ця людина може знати інформацію, яка їм потрібна, у людей, яких вони наймають, але це все.
Тож, коли їм потрібен новий працівник, вони дзвонять в наймане агентство і кажуть їм, що їм потрібно. Тепер, щоб насправді когось найняти , потрібно знати багато речей - переваги, перевірку відповідності тощо. Але особа, яка наймає, не повинна нічого з цього знати - агенція найму займається цим усім.
Таким же чином, використання Фабрики дозволяє споживачеві створювати нові об'єкти, не знаючи деталей того, як вони створені, або які їх залежності - вони повинні лише дати інформацію, яку вони насправді хочуть.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Отже, тепер споживач ThingFactory може отримати річ, не знаючи про залежність речі, за винятком рядкових даних, що надходять від споживача.
within an object instead of a Factory class
. Я думаю, що він мав на увазі сценарій, коли ви робите ctor приватним і використовуєте статичний метод для екземпляра класу (створення об'єкта). Але, щоб наслідувати цей приклад, потрібно ThingFactory
спочатку інстанціювати клас, щоб отримати Thing
об’єкти, що робить це Factory class
фактично.
Фабричні методи слід розглядати як альтернативу конструкторам - здебільшого, коли конструктори недостатньо виразні, тобто.
class Foo{
public Foo(bool withBar);
}
не так виразно, як:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Фабричні класи корисні, коли вам потрібен складний процес побудови об'єкта, коли для будівництва потрібна залежність, яку ви не хочете для фактичного класу, коли вам потрібно побудувати різні об'єкти тощо.
Одна з ситуацій, коли я особисто знаходжу окремі класи Фабрики, щоб мати сенс, коли кінцевий об'єкт, який ви намагаєтесь створити, спирається на кілька інших об'єктів. Наприклад, в 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
абстрагується (оскільки вам не потрібно створювати та встановлювати кожну залежність, коли ви просто хочете створити a House
), і в той же час централізований, що полегшує обслуговування. Є й інші причини, чому використання окремих Фабрик може бути корисним (наприклад, заповідність), але я вважаю цей конкретний випадок використання, щоб найкращим чином проілюструвати, як фабричні класи можуть бути корисними.
HouseFactory
класу?
create
методу. Наприклад, якщо у вас House
завжди буде однаковий вид, LivingRoom
тоді може бути сенс, щоб його парами були твердо кодованими у заводському класі, а не передавались як аргументи. Або ви можете надати type
аргумент своєму HouseFactory::create
методу, якщо у вас є кілька типів LivingRoom
s і у вас є комутатор всередині з параметрами, жорстко кодованими для кожного типу.
Важливо чітко розмежувати ідею, використовуючи заводський чи заводський метод. Обидва призначені для вирішення взаємовиключних проблем різного типу об'єктів.
Давайте конкретизуємо "заводський метод":
Перше, що ви розробляєте бібліотеку чи 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();
}
}
Добре використовувати фабричні методи всередині об'єкта, коли:
Добре використовувати абстрактний заводський клас, коли:
UML від
Продукт: Він визначає інтерфейс об'єктів, створених заводським методом.
ConcreteProduct: реалізує інтерфейс продукту
Творець: оголошує заводський метод
ConcreateCreator: реалізує заводський метод для повернення екземпляра 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
клас, застосовуючи a FactoryMethod
.
Game
- це інтерфейс для всіх типів ігор. Він визначає складний метод:createGame()
Chess, Ludo, Checkers
- це різні варіанти ігор, які забезпечують реалізацію createGame()
public Game getGame(String gameName)
знаходиться FactoryMethod
в IGameFactory
класі
GameFactory
попередньо створює різні ігри в конструкторі. Він реалізує IGameFactory
заводський метод.
Ім'я гри передається як аргумент командного рядка NotStaticFactoryDemo
getGame
у програмі GameFactory
приймає назву гри та повертає відповідний Game
об’єкт.
Фабрика:
Створює об'єкти, не піддаючи клієнту логіку інстанції.
FactoryMethod
Визначте інтерфейс для створення об'єкта, але нехай підкласи вирішують, який клас інстанціювати. Фабричний метод дозволяє відкладати екземпляр класу для підкласів
Корпус:
Коли використовувати: Client
не знає, які конкретні класи потрібно буде створити під час виконання, але просто хоче отримати клас, який зробить цю роботу.
getArea()
НЕ фабричний метод взагалі .
Це справді справа смаку. Заводські класи можуть бути абстраговані / пов’язані між собою за необхідності, тоді як фабричні методи мають меншу вагу (а також мають тенденцію до перевірки, оскільки вони не мають визначеного типу, але для них потрібен відомий пункт реєстрації, схожий на службу локатор, але для визначення заводських методів).
Фабричні класи корисні, коли тип об'єкта, який вони повертають, має приватний конструктор, коли різні фабричні класи встановлюють різні властивості на об'єкт, що повертається, або коли певний заводський тип поєднується з його типом бетону, що повертається.
WCF використовує класи ServiceHostFactory для отримання об’єктів ServiceHost в різних ситуаціях. Стандартний ServiceHostFactory використовується IIS для отримання екземплярів ServiceHost для файлів .svc , але WebScriptServiceHostFactory використовується для сервісів, які повертають серіалізації клієнтам JavaScript. Служби даних ADO.NET мають свої спеціальні DataServiceHostFactory, а ASP.NET має свою ApplicationServicesHostFactory, оскільки в її службах є приватні конструктори.
Якщо у вас є лише один клас, який споживає фабрику, то ви можете просто використовувати заводський метод у цьому класі.
Розглянемо сценарій, коли потрібно розробити замовлення та клас клієнтів. Для простоти та початкових вимог ви не відчуваєте потреби в заводських класах для замовлення та наповнюєте свою заявку багатьма заявами "new Order ()". Речі працюють добре.
Тепер з'являється нова вимога, що об'єкт "Замовлення" неможливо встановити без асоціації клієнтів (нова залежність). Тепер у вас є наступні міркування.
1- Ви створюєте конструкторське перевантаження, яке працюватиме лише для нових реалізацій. (Не прийнятний). 2- Ви змінюєте підписи Order () та змінюєте кожну інкасацію. (Недобра практика та справжній біль).
Натомість Якщо ви створили фабрику для класу замовлення, вам потрібно змінити лише один рядок коду, і ви готові йти. Я пропоную заводський клас майже для кожного об'єднаного об'єднання. Сподіваюся, що це допомагає.
якщо ви хочете створити інший об’єкт з точки зору використання. Це корисно.
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;
}
}
Будь-який клас, який визначає створення об'єкта його підкласу для об'єкта, з яким він повинен працювати, може розглядатися як приклад заводського шаблону.
Я детально згадав в іншій відповіді за адресою https://stackoverflow.com/a/49110001/504133
Я думаю, це буде залежати від ступеня зв'язаного зв'язку, яку ви хочете внести до свого коду.
Фабричний метод дуже добре роз’єднує речі, але заводський клас немає.
Іншими словами, змінити речі простіше, якщо ви використовуєте заводський метод, ніж якщо ви використовуєте просту фабрику (відомий як заводський клас).
Подивіться на цей приклад: https://connected2know.com/programming/java-factory-pattern/ . А тепер уявіть, що ви хочете привезти нову Тварину. У класі Factory потрібно змінити Factory, але у заводському методі - ні, вам потрібно лише додати новий підклас.
Заводські класи більш важкі, але дають певні переваги. У випадках, коли вам потрібно створити об'єкти з декількох необроблених джерел даних, вони дозволяють вам інкапсулювати лише логіку побудови (а може бути і агрегацію даних) в одному місці. Там його можна перевірити абстрактно, не торкаючись об’єктного інтерфейсу.
Я вважаю це корисною схемою, особливо там, де я не в змозі замінити і неадекватний ORM і хочу ефективно інстанціювати багато об'єктів з таблиці приєднань та збережених процедур.
Я порівнюю фабрики з концепцією бібліотек. Наприклад, ви можете мати бібліотеку для роботи з числами та іншу для роботи з фігурами. Ви можете зберігати функції цих бібліотек у логічно названих каталогах як Numbers
або Shapes
. Це загальні типи, які можуть включати цілі числа, поплавці, добули, довгі або прямокутники, кола, трикутники, п'ятикутники у випадку фігур.
Фабрика використовує поліморфізм, ін'єкцію залежності та інверсію контролю.
Зазначене призначення заводських моделей: 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 );
NumbersFactory::makeNumber( 'float', 12.5 );
дає мені просто сказати, new Float(12.5);
якщо я знаю, що мені потрібно Float
? Це те, що я не розумію на заводах ... в чому справа?