it-swarm-eu.dev

Fabrikmuster. Wann werkseitige Methoden verwenden?

Wann ist es sinnvoll, Factory-Methoden innerhalb eines Objekts anstelle einer Factory-Klasse zu verwenden?

249
jjshell

Ich denke gerne über Designmuster nach, wenn meine Klassen „Menschen“ sind, und die Muster sind die Art und Weise, wie die Menschen miteinander reden.

Für mich ist das Fabrikmuster also wie eine Personalagentur. Sie haben jemanden, der eine variable Anzahl von Arbeitern benötigt. Diese Person kann einige Informationen kennen, die sie in den Leuten benötigt, die sie anstellen, aber das ist es.

Wenn sie also einen neuen Mitarbeiter brauchen, rufen sie das Personalbüro an und teilen ihnen mit, was sie brauchen. Nun, um tatsächlich einstellen jemanden zu haben, müssen Sie eine Menge Dinge wissen - Vorteile, Berechtigungsüberprüfung usw. Aber die anstellende Person muss nichts davon wissen - die anstellende Agentur kümmert sich um alles Das.

Auf die gleiche Weise ermöglicht die Verwendung einer Factory dem Konsumenten, neue Objekte zu erstellen, ohne die Details zu kennen, wie sie erstellt wurden oder welche Abhängigkeiten sie haben - er muss nur die Informationen angeben, die er tatsächlich haben möchte.

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

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

Jetzt kann der Konsument der ThingFactory eine Sache erhalten, ohne die Abhängigkeiten der Sache kennen zu müssen, mit Ausnahme der Zeichenfolgendaten, die vom Konsumenten stammen.

369
kyoryu

Factory-Methoden sollten als Alternative zu Konstruktoren betrachtet werden - meistens, wenn Konstruktoren nicht ausdrucksstark genug sind, dh.

class Foo{
  public Foo(bool withBar);
}

ist nicht so ausdrucksstark wie:

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

Factory-Klassen sind nützlich, wenn Sie einen komplizierten Prozess zum Erstellen des Objekts benötigen, wenn die Konstruktion eine Abhängigkeit benötigt, die Sie für die eigentliche Klasse nicht benötigen, wenn Sie verschiedene Objekte erstellen usw.

84
Rasmus Faber

Eine Situation, in der ich persönlich getrennte Factory-Klassen finde, ist sinnvoll, wenn das endgültige Objekt, das Sie erstellen möchten, von mehreren anderen Objekten abhängt. Zum Beispiel in PHP: Nehmen wir an, Sie haben ein House-Objekt, das wiederum ein Kitchen- und ein LivingRoom-Objekt hat, und das LivingRoom-Objekt enthält ebenfalls ein TV-Objekt. 

Die einfachste Methode, um dies zu erreichen, besteht darin, dass jedes Objekt seine untergeordneten Elemente mit seiner Konstruktionsmethode erstellt. Wenn die Eigenschaften jedoch relativ verschachtelt sind, wird die Erstellung Ihrer House wahrscheinlich einige Zeit in Anspruch nehmen, um genau zu ermitteln, was fehlgeschlagen ist.

Alternativ können Sie Folgendes tun (Abhängigkeitsinjektion, wenn Ihnen der Phantasiebegriff gefällt):

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

Wenn hier der Vorgang des Erstellens einer House fehlschlägt, gibt es nur einen Ort, an dem Sie nachschauen müssen, aber dieses Stück jedes Mal verwenden zu müssen, wenn Sie eine neue House wünschen, ist alles andere als bequem. Geben Sie die Fabriken ein:

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

Dank der Factory hier ist der Prozess des Erstellens einer House abstrahiert (dh Sie müssen nicht jede einzelne Abhängigkeit erstellen und einrichten, wenn Sie nur eine House erstellen möchten) und gleichzeitig zentralisiert, was die Wartung erleichtert . Es gibt andere Gründe, warum die Verwendung separater Factories von Vorteil sein kann (z. B. Testbarkeit), aber ich finde diesen speziellen Anwendungsfall, um am besten zu veranschaulichen, wie Factory-Klassen nützlich sein können.

66
Mahn

Es ist wichtig, die zugrunde liegende Idee mit Hilfe der Factory- oder Factory-Methode klar zu unterscheiden .. Beide sollen sich gegenseitig ausschließende unterschiedliche Arten der Objekterstellungsprobleme ansprechen.

Lassen Sie uns "Fabrikmethode" spezifizieren:

Das Erste ist, dass, wenn Sie Bibliotheken oder APIs entwickeln, die wiederum für die weitere Anwendungsentwicklung verwendet werden, die Factory-Methode eine der besten Optionen für das Erstellungsmuster ist. Grund dahinter; Wir wissen, dass, wann ein Objekt mit den erforderlichen Funktionen erstellt wird, der Objekttyp jedoch unentschieden bleibt oder ob die Übergabe von dynamischen Parametern entschieden wird .

Der Punkt ist, dass ungefähr das gleiche durch Verwendung des Factory-Patterns erreicht werden kann. Ein gewaltiger Nachteil führt jedoch dazu, dass das Factory-Pattern für das oben hervorgehobene Problem verwendet wird. Es ist jedoch so, dass Ihre Logik beim Erstellen verschiedener Objekte (Unterklassenobjekte) verwendet wird Seien Sie spezifisch für bestimmte geschäftliche Bedingungen. Wenn Sie also die Funktionalität Ihrer Bibliothek für andere Plattformen erweitern möchten, müssen Sie in Zukunft mehr Unterklassen der Basisschnittstelle oder der abstrakten Klasse hinzufügen, sodass factory diese Objekte zusätzlich zu den vorhandenen zurückgibt basierend auf einigen dynamischen Parametern) dann jedes Mal, wenn Sie die Logik der Factory-Klasse ändern (erweitern) müssen, was teuer ist und aus Design-Sicht nicht gut ist .. Auf der anderen Seite, wenn das "Factory-Methode" -Muster verwendet wird Um dasselbe zu tun, müssen Sie lediglich zusätzliche Funktionen (Unterklassen) erstellen und diese dynamisch durch Injektion registrieren lassen, ohne dass Änderungen am Basiscode vorgenommen werden müssen.

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

Es ist ratsam, Factory-Methoden Inside-Objekt zu verwenden, wenn:

  1. Die Klasse des Objekts kennt nicht die genauen Unterklassen, die erstellt werden müssen
  2. Die Klasse des Objekts ist so gestaltet, dass die von ihm erstellten Objekte durch Unterklassen angegeben wurden
  3. Die Klasse von Object delegiert ihre Aufgaben an Hilfsklassen und weiß nicht, welche Klasse diese Aufgaben übernimmt

Es ist eine gute Idee, abstract factory class zu verwenden, wenn:

  1. Ihr Objekt sollte nicht davon abhängen, wie seine inneren Objekte erstellt und gestaltet werden
  2. Eine Gruppe verknüpfter Objekte sollte zusammen verwendet werden, und Sie müssen diese Einschränkung erfüllen
  3. Das Objekt sollte von einer von mehreren möglichen Familien von verknüpften Objekten konfiguriert werden, die Teil Ihres übergeordneten Objekts sind
  4. Es ist erforderlich, untergeordnete Objekte gemeinsam zu nutzen, die nur Schnittstellen zeigen, aber keine Implementierung
14
Dzianis Yafimau

Sie sind auch nützlich, wenn Sie mehrere "Konstruktoren" mit demselben Parametertyp, aber unterschiedlichem Verhalten benötigen. 

14
Rik

UML von 

 enter image description here

Product: Definiert eine Schnittstelle der Objekte, die die Factory-Methode erstellt.

ConcreteProduct: Implementiert die Produktschnittstelle

Creator: Deklariert die Factory-Methode

ConcreateCreator: Implementiert die Factory-Methode, um eine Instanz eines ConcreteProduct zurückzugeben

Problemaussage: Erstellen Sie eine Factory of Games mit Factory Methods, die die Spieloberfläche definiert.

Code-Auszug:

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

ausgabe:

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

Dieses Beispiel zeigt eine Factory-Klasse, indem eine FactoryMethod implementiert wird.

  1. Game ist die Schnittstelle für alle Arten von Spielen. Es definiert eine komplexe Methode: createGame()

  2. Chess, Ludo, Checkers sind verschiedene Spielvarianten, die die Implementierung von createGame() ermöglichen.

  3. public Game getGame(String gameName) ist FactoryMethod in der IGameFactory-Klasse

  4. GameFactory erstellt verschiedene Arten von Spielen im Konstruktor vor. Es implementiert die IGameFactory Factory-Methode. 

  5. spielname wird als Befehlszeilenargument an NotStaticFactoryDemo übergeben

  6. getGame in GameFactory akzeptiert einen Spielnamen und gibt das entsprechende Game-Objekt zurück.

Fabrik:

Erstellt Objekte, ohne dem Client die Instantiierungslogik zugänglich zu machen.

FactoryMethod

Definieren Sie eine Schnittstelle zum Erstellen eines Objekts, aber lassen Sie die Unterklassen entscheiden, welche Klasse instanziiert werden soll. Mit der Factory-Methode kann eine Klasse die Instantiierung auf Unterklassen verschieben

Anwendungsfall:

Verwendungszweck: Client weiß nicht, welche konkreten Klassen zur Laufzeit erstellt werden müssen, sondern möchte nur eine Klasse abrufen, die die Aufgabe erfüllt.

9
Ravindra babu

Es ist wirklich Geschmackssache. Factory-Klassen können je nach Bedarf abstrahiert/zusammengeführt werden, während Factory-Methoden leichter sind (und auch dazu neigen, testbar zu sein, da sie keinen definierten Typ haben, sie erfordern jedoch einen bekannten Registrierungspunkt, ähnlich einem Dienst locator, aber zum Auffinden von Fabrikmethoden).

5
Brad Wilson

Factory-Klassen sind nützlich, wenn der von ihnen zurückgegebene Objekttyp über einen privaten Konstruktor verfügt, wenn verschiedene Factory-Klassen unterschiedliche Eigenschaften für das zurückkehrende Objekt festlegen oder wenn ein bestimmter Factory-Typ mit seinem zurückkehrenden Betontyp gekoppelt ist. 

WCFverwendet ServiceHostFactory-Klassen zum Abrufen von ServiceHost-Objekten in verschiedenen Situationen. Die Standard-ServiceHostFactory wird von IIS verwendet, um ServiceHost-Instanzen für .svc -Dateien abzurufen, aber eine WebScriptServiceHostFactory wird für Dienste verwendet, die Serialisierungen an JavaScript-Clients zurückgeben. ADO.NET Data Services verfügt über eine eigene DataServiceHostFactory, und ASP.NET verfügt über ApplicationServicesHostFactory, da seine Dienste über private Konstruktoren verfügen.

Wenn Sie nur eine Klasse haben, die die Factory verbraucht, können Sie einfach eine Factory-Methode innerhalb dieser Klasse verwenden.

4
Mark Cidade

Stellen Sie sich ein Szenario vor, wenn Sie eine Auftrags- und eine Kundenklasse entwerfen müssen. Aus Gründen der Einfachheit und der anfänglichen Anforderungen besteht keine Notwendigkeit, Factory für Order class zu verwenden und Ihre Anwendung mit vielen 'new Order ()' - Anweisungen zu füllen. Die Dinge funktionieren gut.

Nun kommt eine neue Anforderung ins Bild, dass das Auftragsobjekt ohne Kundenbeziehung (neue Abhängigkeit) nicht instanziiert werden kann. Nun haben Sie folgende Überlegungen.

1- Sie erstellen eine Konstruktorüberladung, die nur für neue Implementierungen funktioniert. (Nicht akzeptabel) . 2- Sie ändern die Order () - Signaturen und ändern jede Aufforderung. (Keine gute Praxis und echte Schmerzen).

Wenn Sie eine Factory für Order Class erstellt haben, müssen Sie nur eine Codezeile ändern, und Sie können loslegen. Ich empfehle für fast jeden Aggregatverband die Werksklasse. Hoffentlich hilft das.

3
Muhammad Awais

Gemäß der Sourcing-Website sind die folgenden Ziele:

  • Definieren Sie eine Schnittstelle zum Erstellen eines Objekts. Lassen Sie die Unterklassen entscheiden, welche Klasse instanziiert werden soll. Mit der Factory-Methode kann eine Klasse die Instantiierung auf Unterklassen verschieben.

  • Definieren eines "virtuellen" Konstruktors.

  • Der neue Betreiber wurde als schädlich eingestuft.

Ein Beispiel, wie es verwendet werden kann:

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

Und dann das Testen:

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

Jede Klasse, die die Objekterstellung für das Objekt, für das sie arbeiten muss, auf ihre Unterklasse verlagert, kann als Beispiel für das Factory-Muster angesehen werden.

Ich habe in einer anderen Antwort ausführlich unter https://stackoverflow.com/a/49110001/504133 erwähnt

1
nits.kk

Ich vergleiche Fabriken mit dem Konzept von Bibliotheken. Sie können beispielsweise eine Bibliothek zum Arbeiten mit Zahlen und eine weitere zum Arbeiten mit Formen haben. Sie können die Funktionen dieser Bibliotheken in logisch benannten Verzeichnissen als Numbers oder Shapes speichern. Hierbei handelt es sich um generische Typen, die Ganzzahlen, Floats, Dobules, Longs oder Rechtecke, Kreise, Dreiecke und Fünfecke bei Formen umfassen können.

Der Werksetzer verwendet Polymorphismus, Abhängigkeitseinspritzung und Inversion der Kontrolle.

Der angegebene Zweck der Fabrikmuster ist: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Nehmen wir an, Sie bauen ein Betriebssystem oder Framework und alle einzelnen Komponenten.

Hier ist ein einfaches Beispiel für das Konzept des Factory Patterns in PHP. Ich bin vielleicht nicht zu 100% bei all dem, aber es soll als einfaches Beispiel dienen. Ich bin kein Experte.

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

Werksklassen sind schwerer, bieten aber gewisse Vorteile. Wenn Sie Ihre Objekte aus mehreren Rohdatenquellen erstellen müssen, können Sie nur die Gebäudelogik (und möglicherweise die Aggregation der Daten) an einer Stelle kapseln. Dort kann es abstrakt getestet werden, ohne sich um die Objektschnittstelle zu kümmern.

Ich habe festgestellt, dass dies ein nützliches Muster ist, insbesondere wenn ich ORM nicht ersetzen kann und viele Objekte aus DB-Tabellenverknüpfungen oder gespeicherten Prozeduren effizient instanziieren möchte.

0
jonfm

AbstractFactory Beispiel.

    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

wenn Sie ein anderes Objekt erstellen möchten. Es ist nützlich.

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