it-swarm-eu.dev

Tovární Vzor. Kdy použít tovární metody?

Kdy je vhodné použít tovární metody v objektu namísto třídy Factory?

249
jjshell

Mám rád přemýšlet o designu pattens, pokud jde o mé třídy jsou "lidé", a vzory jsou způsoby, jak lidé mluví spolu navzájem.

Takže pro mě je tovární vzor jako agentura. Máte někoho, kdo bude potřebovat variabilní počet pracovníků. Tato osoba může znát nějaké informace, které potřebují v lidech, které najímají, ale to je vše.

Takže, když potřebují nového zaměstnance, zavolají nájemní agentuře a řeknou jim, co potřebují. Nyní, aby se vlastně najmout někdo, musíte znát spoustu věcí - výhody, ověření způsobilosti, atd. Ale osoba, která najímá, nemusí vědět nic z toho - nájemní agentura to všechno zvládá .

Stejným způsobem, použití Factory umožňuje spotřebiteli vytvářet nové objekty, aniž by museli znát podrobnosti o tom, jak jsou vytvořeny, nebo jaké jsou jejich závislosti - musí pouze poskytnout informace, které skutečně chtějí.

public interface IThingFactory
{
    Thing GetThing(string theString);
}

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

Takže nyní může uživatel ThingFactory získat Věc, aniž by musel vědět o závislostech Věce, s výjimkou řetězcových dat, která pochází od spotřebitele.

355
kyoryu

Tovární metody by měly být považovány za alternativu ke konstruktérům - většinou, když konstruktéři nejsou dostatečně expresivní, tzn.

class Foo{
  public Foo(bool withBar);
}

není tak expresivní jako:

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

Tovární třídy jsou užitečné, když potřebujete složitý proces pro konstrukci objektu, když konstrukce potřebuje závislost, kterou nechcete pro skutečnou třídu, když potřebujete postavit různé objekty atd.

84
Rasmus Faber

Jedna situace, kdy jsem osobně najít samostatné Factory třídy, aby smysl, je, když se konečný objekt, který se snažíte vytvořit, opírá o několik dalších objektů. V PHP: Předpokládejme, že máte objekt House, který má objekt Kitchen a LivingRoom, a objekt LivingRoom má také objekt TV uvnitř. 

Nejjednodušší způsob, jak toho dosáhnout, je, aby každý objekt vytvořil své děti na své konstrukční metodě, ale pokud jsou vlastnosti relativně vnořené, když vaše House selhává, pravděpodobně strávíte nějaký čas snahou izolovat přesně to, co selhává.

Alternativou je následující (injekční závislost, pokud se vám líbí fantázní termín):

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

Pokud proces vytváření House selže, je zde jen jedno místo, kde se dívat, ale použití tohoto bloku pokaždé, když chce nový House, není zdaleka pohodlné. Vstupte do továrny:

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();

Díky továrně je zde proces vytváření House abstrahován (v tom, že nemusíte vytvářet a nastavovat každou závislost, když chcete vytvořit House) a zároveň centralizovat, což usnadňuje údržbu . Existují i ​​jiné důvody, proč může být použití samostatných závodů prospěšné (např. Testovatelnost), ale tento konkrétní případ použití je pro ilustraci nejlepší, jak mohou být třídy Factory užitečné.

66
Mahn

Je důležité jasně odlišit myšlenku za použití tovární nebo tovární metody. Oba mají za cíl řešit vzájemně se vylučující různé druhy problémů s tvorbou objektů.

Buďme specifičtí na "tovární metodu":

První věc je, že když vyvíjíte knihovnu nebo API, které budou zase použity pro další vývoj aplikací, pak je tovární metoda jednou z nejlepších možností pro vytvoření vzoru. Důvod; Víme, že když vytvoříte objekt požadované funkce, ale typ objektu zůstane nerozhodnut nebo bude rozhodnuto, že dynamické parametry budou předány .

Teď jde o to, že přibližně stejné lze dosáhnout použitím samotného továrního vzoru, ale jedna obrovská nevýhoda se zavede do systému, pokud bude tovární vzor použit pro výše zvýrazněný problém, to je, že vaše logika vytváření různých objektů (objektů pod tříd) bude být specifický pro některé obchodní podmínky, takže v budoucnu, když potřebujete rozšířit funkčnost vaší knihovny pro jiné platformy (technicky je třeba přidat více dílčích tříd základního rozhraní nebo abstraktní třídy, takže továrna tyto objekty vrátí také k existujícím) na základě některých dynamických parametrů) pak pokaždé, když potřebujete změnit (rozšířit) logiku tovární třídy, která bude nákladná operace a není dobrá z hlediska designu. Na druhé straně, pokud bude vzor „tovární metoda“ použít k provedení stejné věci, pak stačí vytvořit další funkce (podtřídy) a dostat je registrován dynamicky injekcí, která nevyžaduje změny v základním kódu.

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();
    }
}
16
Prakash Chhipa

Je vhodné použít tovární metody vnitřní objekt, když:

  1. Třída objektu neví, jaké přesné podtřídy musí vytvořit
  2. Třída objektu je navržena tak, aby vytvořené objekty byly zadány podtřídy
  3. Třída objektu deleguje své povinnosti na pomocné podtřídy a neví, jakou přesnou třídu budou mít tyto povinnosti

Je vhodné použít abstraktní továrna třída, když:

  1. Váš objekt by neměl záviset na tom, jak jsou vytvořeny a navrženy jeho vnitřní objekty
  2. Skupina propojených objektů by měla být používána společně a toto omezení musíte použít
  3. Objekt by měl být konfigurován jednou z několika možných skupin propojených objektů, které budou součástí rodičovského objektu
  4. Je nutné sdílet podřízené objekty zobrazující pouze rozhraní, ale nikoli implementaci
14
Dzianis Yafimau

Jsou také užitečné, když potřebujete několik "konstruktorů" se stejným typem parametrů, ale s odlišným chováním. 

14
Rik

UML 

 enter image description here

Produkt: Definuje rozhraní objektů, které vytvoří metoda Factory.

ConcreteProduct: Implementuje rozhraní produktu

Creator: Prohlašuje metodu Factory

ConcreateCreator: Realizuje metodu Factory k návratu instance ConcreteProduct

Prohlášení o problému: Vytvořte továrnu her pomocí Factory Methods, která definuje herní rozhraní.

Fragment kódu:

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");
        }           
    }
}

výstup:

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

Tento příklad ukazuje třídu Factory implementací FactoryMethod.

  1. Game je rozhraní pro všechny typy her. Definuje komplexní metodu: createGame()

  2. Chess, Ludo, Checkers jsou různé varianty her, které poskytují implementaci do createGame()

  3. public Game getGame(String gameName) je FactoryMethod ve třídě IGameFactory

  4. GameFactory pre-vytváří různé typy her ve konstruktoru. Realizuje IGameFactory tovární metodu. 

  5. game Jméno je předáno jako argument příkazového řádku NotStaticFactoryDemo

  6. getGame v GameFactory přijme název hry a vrátí odpovídající objekt Game.

Továrna:

Vytvoří objekty bez vystavení logiky zprostředkování klientovi.

FactoryMethod

Definujte rozhraní pro vytvoření objektu, ale nechte podtřídy rozhodnout, kterou třídu chcete vytvořit. Metoda Factory umožňuje třídě odložit instanci na podtřídy

Případ použití:

Kdy použít: Client neví, jaké konkrétní třídy bude nutné vytvořit za běhu, ale chce jen získat třídu, která bude dělat svou práci.

9
Ravindra babu

Je to opravdu otázka chuti. Tovární třídy mohou být podle potřeby odstraněny/propojeny, zatímco tovární metody jsou lehčí (a také bývají testovatelné, protože nemají definovaný typ, ale budou vyžadovat známý registrační bod, podobný službě lokátor, ale pro vyhledání výrobních metod).

5
Brad Wilson

Tovární třídy jsou užitečné, když typ objektu, který se vrací, má soukromý konstruktor, když různé tovární třídy nastavují různé vlastnosti na vracejícím se objektu nebo když je určitý typ továrny spojen s jeho vracejícím se betonovým typem. 

WCFpoužívá třídy ServiceHostFactory k načtení objektů ServiceHost v různých situacích. Standardní služba ServiceHostFactory je použita IIS k načtení instancí ServiceHost pro soubory .svc, ale WebScriptServiceHostFactory se používá pro služby, které vracejí serializace klientům JavaScriptu. ADO.NET Data Services má vlastní speciální DataServiceHostFactory a ASP.NET má své ApplicationServicesHostFactory, protože jeho služby mají soukromé konstruktéry.

Pokud máte pouze jednu třídu, která spotřebovává továrnu, pak můžete použít pouze tovární metodu v rámci této třídy.

4
Mark Cidade

Zvažte scénář, když máte navrhnout třídu Objednávka a Zákazník. Pro jednoduchost a počáteční požadavky necítíte potřebu továrny na třídu objednávek a vyplňte svou aplikaci mnoha „novými objednávkami“. Věci fungují dobře.

Nyní nový obrázek vstoupí do obrázku, že objekt objednávky nemůže být vytvořen bez přidružení zákazníků (nová závislost). Nyní máte následující úvahy.

1- Vytvoříte přetížení konstruktoru, které bude fungovat pouze pro nové implementace. (Nepřijatelné). 2- Změníte příkazy () a změníte každou výzvu. (Není to dobrá praxe a skutečná bolest).

Místo toho Pokud jste vytvořili továrnu pro třídu objednávek, musíte změnit pouze jeden řádek kódu a vy se můžete vydat. Doporučuji třídu Factory pro téměř každou agregovanou asociaci. Doufám, že to pomůže.

3
Muhammad Awais

Podle webových stránek o tvorbě zdrojů jsou záměry:

  • Definujte rozhraní pro vytvoření objektu, ale nechte podtřídy rozhodnout, která třída se má vytvořit. Factory Method umožňuje třídě odložit instanci na podtřídy.

  • Definování "virtuálního" konstruktoru.

  • Nový operátor je považován za škodlivý.

Příklad použití:

abstract class AbstractFactoryMethod {
    abstract function makePHPBook($param);
}

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

    return $book;
}

A pak testování:

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

echo 'Testing OReillyFactoryMethod';
$factoryMethodInstance = new OReillyFactoryMethod();
testFactoryMethod($factoryMethodInstance);
3
Alexander Beat

Jakákoli třída, která odkládá vytvoření objektu na jeho podtřídu pro objekt, se kterým má pracovat, může být viděna jako příklad vzoru Factory.

Podrobně jsem se zmínil v další odpovědi na https://stackoverflow.com/a/49110001/504133

1
nits.kk

Přirovnávám továrny k pojetí knihoven. Například můžete mít knihovnu pro práci s čísly a jinou pro práci s tvary. Funkce těchto knihoven můžete ukládat do logicky pojmenovaných adresářů jako Numbers nebo Shapes. Jedná se o obecné typy, které mohou zahrnovat celá čísla, plováky, dobules, longs nebo obdélníky, kruhy, trojúhelníky, pětiúhelníky v případě tvarů.

Výrobní petter používá polymorfismus, závislostní injekci a inverzi kontroly.

Uvedeným účelem výrobních vzorů je: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Řekněme, že budujete operační systém nebo rámec a budujete všechny jednotlivé komponenty.

Zde je jednoduchý příklad konceptu Factory Pattern v PHP. Nemůžu být 100% na to všechno, ale je to má sloužit jako jednoduchý příklad. Nejsem expert.

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 );
0
Robert Rocha

Tovární třídy jsou těžší, ale poskytují vám určité výhody. V případech, kdy potřebujete stavět objekty z více zdrojů surových dat, můžete zapouzdřit pouze logiku budovy (a možná agregaci dat) na jednom místě. Tam to může být testováno abstraktně, aniž by se zabýval objektovým rozhraním.

Našel jsem to užitečný vzor, ​​zejména tam, kde nejsem schopen nahradit a neadekvátní ORM a chcete efektivně vytvořit instanci mnoha objektů z DB tabulky spojení nebo uložené procedury.

0
jonfm

Příklad AbstractFactory.

    TypeImpl<String> type = new TypeImpl<>();
    type.addType("Condition");
    type.addType("Hazardous");

    AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) {

        @Override
        public Tag create(String string) {
            String tp = type.find(string);

            switch (tp) {
                case "Hazardous":
                    return new HazardousTag();
                case "Condition":
                    return new ConditionTag();
                default:
                    return null;
            }
        }
    };

    Tag tagHazardous = tags.create("Hazardous");
    Tag tagCondition = tags.create("Condition");

}
0
Vahe Gharibyan

chcete-li vytvořit jiný objekt z hlediska použití. Je to užitečné.

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;
      }
}
0
Samet öztoprak