620 Stimmen

Was sind die Unterschiede zwischen den Entwurfsmustern Abstract Factory und Factory?

Ich weiß, dass es viele Beiträge über die Unterschiede zwischen diesen beiden Mustern gibt, aber es gibt ein paar Dinge, die ich nicht finden kann.

Aus dem, was ich gelesen habe, sehe ich, dass die Fabrik-Methode Muster ermöglicht es Ihnen zu definieren, wie ein einzelnes konkretes Produkt zu schaffen, sondern versteckt die Umsetzung aus dem Client, wie sie ein generisches Produkt sehen wird. Meine erste Frage bezieht sich auf die abstrakte Fabrik. Erlaubt sie die Erstellung von Familien konkreter Objekte (die von der verwendeten Fabrik abhängen können) und nicht nur eines einzelnen konkreten Objekts? Gibt die abstrakte Fabrik nur ein sehr großes Objekt zurück oder viele Objekte, je nachdem, welche Methoden Sie aufrufen?

Meine letzten beiden Fragen beziehen sich auf ein einziges Zitat, das ich nicht ganz verstehe und das ich an zahlreichen Stellen gesehen habe:

Ein Unterschied zwischen den beiden ist, dass mit dem abstrakten Fabrikmuster, eine Klasse die Verantwortung für die Objektinstanziierung an ein anderes Objekt durch Komposition delegiert, während das Factory Methoden-Muster Vererbung verwendet und eine Unterklasse für die gewünschte gewünschten Objektinstanziierung.

Ich verstehe, dass das Factory-Methodenmuster eine Creator-Schnittstelle hat, die den ConcreteCreator wissen lässt, welches ConcreteProduct er instanziieren soll. Ist es das, was mit der Verwendung von Vererbung zur Handhabung der Objektinstanziierung gemeint ist?

Wie genau delegiert das Muster der abstrakten Fabrik die Verantwortung für die Instanziierung eines Objekts über die Komposition an ein anderes Objekt, um sich auf dieses Zitat zu beziehen? Was ist damit gemeint? In meinen Augen sieht es so aus, als ob das Abstract Factory-Muster auch die Vererbung nutzt, um den Konstruktionsprozess durchzuführen, aber auch hier bin ich noch dabei, etwas über diese Muster zu lernen.

Für jede Hilfe, insbesondere bei der letzten Frage, wäre ich sehr dankbar.

3voto

yoAlex5 Punkte 20661

Fabrik-Design-Muster

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

Fabrik

Anwendungsfall: instanziieren un Gegenstand von generation 2

Es ist ein Creational Muster, das es Ihnen ermöglicht, die generation 2 an einem einfachen Ort. Es ist konform mit SRP und OCP - alle Änderungen werden in einer einzigen Klasse vorgenommen.

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

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

Fabrik-Methode

Anwendungsfall: instanziieren un Gegenstand von generation 3

Hilft bei der Arbeit mit der nächsten Generation von Familienmitgliedern. Jeder Maler hat seinen eigenen Stil wie Impressionismus, Surrealismus... Factory Method verwendet abstrakt Creator als Factory(abstrakte Methode) und Concrete Creators sind Umsetzungen dieser Methode

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

Abstrakte Fabrik

Anwendungsfall: instanziieren todo Objekte von generation 3

Factory ist ein Teil der abstrakten Factory und Verwirklichungen in 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;
    }
}

3voto

KGhatak Punkte 6385

Verstehen Sie die Unterschiede in den Motivationen:

Nehmen wir an, Sie bauen ein Werkzeug, das aus Objekten und einer konkreten Implementierung der Zusammenhänge zwischen den Objekten besteht. Da Sie Variationen in den Objekten vorsehen, haben Sie eine Indirektion geschaffen, indem Sie die Verantwortung für die Erstellung von Varianten der Objekte einem anderen Objekt zugewiesen haben ( wir nennen es abstrakte Fabrik ). Diese Abstraktion ist von großem Nutzen, da Sie voraussehen, dass zukünftige Erweiterungen Varianten dieser Objekte benötigen.

Eine weitere faszinierende Motivation in diesem Gedankengang ist der Fall, dass jedes oder keines der Objekte aus der gesamten Gruppe eine entsprechende Variante hat. Unter bestimmten Bedingungen wird eine der Varianten verwendet, und in jedem Fall müssen alle Objekte derselben Variante angehören. Dies ist vielleicht etwas kontraintuitiv zu verstehen, da wir oft dazu neigen zu denken, dass - solange die Varianten eines Objekts einem gemeinsamen einheitlichen Vertrag folgen ( Schnittstelle im weiteren Sinne ), sollte der konkrete Implementierungscode niemals abbrechen. Interessant ist, dass dies nicht immer der Fall ist, insbesondere wenn das erwartete Verhalten nicht durch einen Programmiervertrag modelliert werden kann.

Eine einfache ( Anlehnung an die Idee von GoF ) ist jede GUI-Anwendung, z. B. ein virtueller Monitor, der das Aussehen von MS-, Mac- oder Fedora-Betriebssystemen emuliert. Wenn z. B. alle Widget-Objekte wie Fenster, Schaltflächen usw. die MS-Variante haben, mit Ausnahme einer Bildlaufleiste, die von der MAC-Variante abgeleitet ist, ist der Zweck des Tools nicht erfüllt.

Diese Fälle bilden den Grundbedarf für Abstraktes Fabrik-Muster .

Andererseits, stellen Sie sich vor, Sie schreiben ein Framework, damit viele Leute verschiedene Werkzeuge bauen können ( wie in den obigen Beispielen ) unter Verwendung Ihres Rahmens. Aufgrund der Idee eines Frameworks müssen Sie das nicht, auch wenn Sie keine konkreten Objekte in Ihrer Logik verwenden könnten. Sie legen vielmehr einige Verträge auf hoher Ebene zwischen verschiedenen Objekten und deren Interaktion fest. Während Sie ( als Rahmenentwickler ) auf einer sehr abstrakten Ebene bleiben, ist jeder Erbauer des Werkzeugs gezwungen, Ihren Rahmenkonstruktionen zu folgen. Allerdings müssen sie ( die Werkzeugbauer ) haben die Freiheit zu entscheiden, welches Objekt gebaut werden soll und wie alle Objekte, die sie erstellen, zusammenwirken werden. Anders als im vorherigen Fall ( des abstrakten Fabrikmusters ), Sie ( als Ersteller des Rahmens ) müssen in diesem Fall nicht mit konkreten Objekten arbeiten, sondern können auf der Vertragsebene der Objekte bleiben. Im Gegensatz zum zweiten Teil der vorangegangenen Begründungen kommen Sie oder die Tool-Builder außerdem nie in die Situation, Objekte aus Varianten zu mischen. Während der Framework-Code hier auf der Vertragsebene bleibt, ist jeder Tool-Builder eingeschränkt ( durch die Art des Falles selbst ) ihre eigenen Objekte zu verwenden. Die Erstellung von Objekten wird in diesem Fall an jeden Implementierer delegiert, und die Framework-Anbieter stellen lediglich einheitliche Methoden zur Erstellung und Rückgabe von Objekten bereit. Solche Methoden sind für Framework-Entwickler unvermeidlich, um mit ihrem Code fortzufahren, und haben einen speziellen Namen namens Fabrik-Methode ( Factory Method Pattern für das zugrunde liegende Muster ).

Wenige Anmerkungen:

  • Wenn Sie mit dem Begriff "Template-Methode" vertraut sind, dann wissen Sie, dass Factory-Methoden bei Programmen, die zu einer Art Framework gehören, oft von Template-Methoden aufgerufen werden. Im Gegensatz dazu sind Template-Methoden von Anwendungsprogrammen oft eine einfache Implementierung eines bestimmten Algorithmus und enthalten keine Factory-Methoden.
  • Der Vollständigkeit halber sei noch erwähnt, dass der Rahmen ( oben genannt ) kann ein Tool-Builder bei der Erstellung eines Tools in jeder Factory-Methode anstelle eines konkreten Objekts die Verantwortung an ein abstraktes Factory-Objekt delegieren, sofern der Tool-Builder Variationen der konkreten Objekte für zukünftige Erweiterungen vorsieht.

Beispiel-Code:

//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
    }
    ….
}

2voto

Adrian Liu Punkte 365

Lassen Sie uns klar, dass die meiste Zeit in der Produktion Code, verwenden wir abstrakte Fabrik Muster, weil Klasse A mit Schnittstelle B programmiert ist. Und A muss Instanzen von B zu erstellen. So A hat ein Fabrik-Objekt, um Instanzen von B zu produzieren.

1voto

Saurabh Punkte 1305

Um es sehr einfach zu machen mit minimaler Schnittstelle & bitte Fokus "//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;
        }
    }

Hier wichtige Punkte: 1. Factory & AbstractFactory Mechanismen müssen Vererbung verwenden (System.Object-> byte, float ...); wenn Sie also Vererbung im Programm haben, dann ist Factory (Abstract Factory würde es wahrscheinlich nicht geben) bereits durch Design vorhanden 2. Der Erzeuger (MyFactory) kennt den konkreten Typ und gibt daher ein Objekt vom konkreten Typ an den Aufrufer (Main) zurück; in der abstrakten Fabrik wäre der Rückgabetyp eine Schnittstelle.

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

Wichtige Punkte: 1. Bedingung: Honda würde "Regular", "Sports" erstellen, aber Hero würde "DarkHorse", "Sports" und "Scooty" erstellen. 2. Warum zwei Schnittstellen? Eine für den Herstellertyp (IVehicleFactory) und eine weitere für die Produktfabrik (IVehicle); eine andere Möglichkeit, 2 Schnittstellen zu verstehen, ist, dass es bei der abstrakten Fabrik um die Erstellung verwandter Objekte geht 2. Der Haken an der Sache ist, dass die Kinder von IVehicleFactory ein IVehicle zurückgeben (anstelle eines konkreten Objekts in der Fabrik); ich erhalte also eine übergeordnete Variable (IVehicle); dann erstelle ich einen konkreten Typ, indem ich CreateSingleVehicle aufrufe und dann das übergeordnete Objekt in ein tatsächliches Kindobjekt umwandle. Was würde passieren, wenn ich RegularBike heroRegularBike = (RegularBike)hero.CreateSingleVehicle("Regular"); Sie werden eine ApplicationException erhalten und deshalb brauchen wir eine generische abstrakte Fabrik, die ich bei Bedarf erläutern werde. Ich hoffe, es hilft Anfängern und Fortgeschrittenen.

1voto

Andy2K11 Punkte 367

Meiner Meinung nach ist die von @TomDalling gegebene Antwort in der Tat richtig (was auch immer sie wert ist), aber es scheint immer noch eine Menge Verwirrung in den Kommentaren zu geben.

Ich habe hier einige leicht untypische Beispiele für die beiden Muster erstellt und versucht, sie auf den ersten Blick recht ähnlich erscheinen zu lassen. Dies wird helfen, die entscheidenden Unterschiede zwischen den beiden Mustern herauszuarbeiten.

Factory Method Abstract Factory

Wenn Sie sich mit den Mustern noch nicht auskennen, sind diese Beispiele wahrscheinlich nicht der beste Startpunkt.

Fabrik-Methode

enter image description here

Client.javaish

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

Schöpfer.javaish

Creator() {}

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

abstract ProductA createProductA();

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

Warum gibt es eine Creator und eine Client ?

Warum nicht? Die FactoryMethod kann mit beiden verwendet werden, aber es wird der Typ von Creator die das spezifische Produkt bestimmt.

Warum ist nicht createProductB abstrakt in Creator ?

Eine Standardimplementierung kann bereitgestellt werden, Unterklassen können die Methode jedoch überschreiben, um ihre eigene Implementierung bereitzustellen.

Ich dachte, Fabrikmethoden erzeugen nur ein Produkt?

Jede Methode gibt nur ein Produkt zurück, aber der Ersteller kann mehrere Fabrikmethoden verwenden, die nicht notwendigerweise in irgendeiner Weise miteinander verbunden sind.

Abstrakte Fabrik

enter image description here

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

Warte! Dein AbstractFactory ist nicht, na ja... äh Abstrakt

Das ist okay, wir bieten immer noch eine Schnittstelle . Die Rückgabetypen für die Erstellungsmethoden sont Supertypen der Produkte, die wir herstellen wollen.

Heiliger Bimbam! Factory2 setzt nicht außer Kraft createProductA() Was ist aus den "Produktfamilien" geworden?

Es gibt nichts im Muster, das besagt, dass ein Objekt nicht zu mehr als einer Familie gehören kann (auch wenn Ihr Anwendungsfall dies vielleicht nicht zulässt). Jede konkrete Fabrik ist dafür verantwortlich zu entscheiden, welche Produkte zusammen erstellt werden dürfen.

Das kann nicht richtig sein, die Client verwendet keine Dependency Injection

Sie müssen entscheiden, was Ihre konkreten Klassen irgendwo sein werden, die Client wird weiterhin in die AbstractFactory Schnittstelle.

Die Verwirrung besteht darin, dass die Menschen Folgendes verwechseln Zusammensetzung con Dependency Injection . Die Client HAS-A AbstractFactory unabhängig davon, wie sie es bekommen hat. Im Gegensatz dazu steht die IS-A-Beziehung, Client y AbstractFactory keine Vererbung zwischen ihnen haben.

Wesentliche Unterschiede

  • Bei Abstract Factory geht es immer um Familien von Objekten
  • Factory Method ist nur eine Methode, die ermöglicht Unterklassen zur Angabe des Typs des konkreten Objekts
  • Die abstrakte Factory bietet eine Schnittstelle zu einem Client und ist unabhängig davon, wo die Produkte verwendet werden. Die Factory-Methode kann vom Creator selbst verwendet oder einem Client zur Verfügung gestellt werden.

概要

El Zweck einer Fabrik ist die Bereitstellung eines Objekts, entweder für einen Kunden oder für sich selbst.

Ein Ersteller hat seine eigenen Verantwortlichkeiten und muss möglicherweise Objekte verwenden oder sie an einen Client weitergeben

Definieren Sie eine Schnittstelle für die Erstellung eines Objekts, aber lassen Sie die Unterklassen entscheiden, welche Klasse instanziert werden soll. Mit der Factory-Methode kann eine Klasse die Instanziierung auf Unterklassen verschieben. - GoF

Nur eine abstrakte Fabrik:

Bietet eine Schnittstelle zur Erstellung von Familien verwandter oder abhängiger Objekte, ohne dass deren konkrete Klassen angegeben werden müssen. - GoF


PlantUML-Code, wenn Sie mit den Diagrammen spielen wollen:

@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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X