Skaliert das abstrakte Fabrikmuster?

8

Ich versuche immer noch, Designmuster hier zu verstehen. Nachdem ich das abstrakte Fabrikmuster gelernt hatte, stellte ich fest, dass dieses Muster nicht gut skaliert werden kann. Schauen Sie sich das Uml-Diagramm des abstrakten Fabrikmusters an

uml Diagramm

Wenn ich ein neues 'AbstractProductC' erstellen muss, muss ich in 'AbstractFactory' eine abstrakte Methode 'CreateProductC' hinzufügen, die sich auf die Implementierung von ConcreateFactory1 und ConcreateFactory2 auswirkt.

Meine Frage hier ist, skaliert Abstract Factory überhaupt (oder) ich denke hier in die falsche Richtung?

Danke im Voraus

Jitendar
quelle
Ich denke, das von Ihnen vorgestellte Diagramm, das relativ normal aussieht, wurde vereinfacht, um das fragliche Muster zu veranschaulichen. Wenn Sie jedoch etwas Komplexeres benötigen, können Sie AbstractFactory immer nur eine Methode definieren lassen: CreateProduct (productType) und dann eine Factory innerhalb einer Factory, die den Typ betrachtet und darauf basierend eine konkrete Klasse instanziiert.
DXM
@ DXM Das behebt das Problem nicht. Sie müssten die Implementierung konkreter Fabriken jedes Mal ändern, wenn Sie ein neues Produkt hinzufügen.
Euphoric
Nun, das einzige, was skaliert werden kann, ist die tatsächliche abstrakte Schnittstelle, aber ja, wenn Sie N Produkte und M Möglichkeiten haben, diese zu erstellen, benötigen Sie N x M Implementierungen, unabhängig davon, wie Sie sie aufteilen, es sei denn, Sie verfolgen eine völlig andere Strategie .
DXM
@Euphoric: Nicht unbedingt. Möglicherweise müssen Sie nur eine Factory-Klasse hinzufügen und diese bei der abstrakten Factory registrieren. Mag ein wenig pingelig erscheinen, aber für Skalierung und SRP finde ich das Hinzufügen etwas weniger mühsam als das Ändern.
Marjan Venema
@MarjanVenema Nun stellt sich die Frage, ob es sich wirklich um eine Fabrik handelt und nicht um ein anderes Muster.
Euphoric

Antworten:

5

Abstract Factory skaliert ganz gut.

Es gibt ein Grundprinzip, das besagt, dass eine Klasse eine Sache gut machen sollte. Ihr Problem hier ist, dass Sie versuchen, viele Klassen dazu zu bringen, viele Dinge zu tun.

Sie müssen sich nicht an eine abstrakte Fabrik halten. Sie können (und sollten) mehrere haben:

AbstractProductAFactorydefiniert die Schnittstelle zur Herstellung von ProductA. Ihre konkreten Implementierungen (ConcreteProductAFactory1, ConcreteProductAFactory2) würden es erweitern.

AbstractProductBFactorydefiniert die Schnittstelle zur Herstellung von ProductB. Ihre konkreten Implementierungen ( ConcreteProductBFactory1, ConcreteProductBFactory2) würden es erweitern.

Wenn Sie dann ein ProductC benötigen, erstellen Sie ein neues AbstractProductCFactory. Sie müssen keine Ihrer anderen Fabriken auf diese Weise ändern.

UPDATE Idealerweise sollte ProductA eine Produktklasse darstellen, dh alle Produkte, die eine Schnittstelle gemeinsam nutzen, die Sie ProductA nennen. In den Kommentaren schlage ich vor, dass dies so etwas wie eine Pizza ist:

interface AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings);
}


class ThinCrustPizzaFactory implements AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings){

        ...

    }
}

class DeepDishPizzaFactory implements AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings){

        ...

    }
}

Und so weiter. Das Hinzufügen einer PanPizzaFactory wirkt sich nicht auf eine der anderen Klassen aus - es handelt sich lediglich um eine neue konkrete Implementierung der PizzaFactory. Wenn Sie Produkte haben, die keine Pizza sind - zum Beispiel Sandwiches -, erstellen Sie dort eine weitere abstrakte Fabrik (z AbstractSandwichFactory. B. ).

Der eigentliche Punkt ist, dass Sie keine abstrakte Fabrik haben möchten, die zwei sehr unterschiedliche Produkttypen mit zwei unterschiedlichen "Build" -Methoden erstellt. Gruppieren Sie sie so logisch wie möglich, damit sie eine Schnittstelle gemeinsam nutzen, und erstellen Sie dann eine abstrakte Factory, die definiert, wie konkrete Fabriken Implementierungen dieser Schnittstelle erstellen sollen.

Matthew Flynn
quelle
4
Du machst Witze oder? Was ist, wenn Ihr Unternehmen 256 Produkte herstellt? Oder 1024?
Robert Harvey
@RobertHarvey - Ich gehe davon aus, dass ProductA eine Produktklasse ist - nehmen wir an, es ist eine AbstractPizzaFactory. Dies kann eine konkrete ThinCrustPizzaFactory, eine konkrete PanPizzaFactory und eine konkrete DeepDishPizzaFactory sein. Ebenso würden Sandwiches nach Subtypen einer AbstractSandwichFactory erstellt. Dies unterscheidet sich von und AgrstractFactory sowohl mit einer buildPizza- als auch einer buildSandwich-Methode.
Matthew Flynn
@RobertHarvey - In der Zwischenzeit können subtile Variationen von Produkten (wie Toppings) separat behandelt werden. Vielleicht wäre Ihre Fabrikmethode AbstractPizzaFactory.buildPizza(List<Topping> toppings).
Matthew Flynn
Vielleicht möchten Sie diese Dinge in Ihrer Antwort klarstellen.
Robert Harvey
@ RobertHarvey - aktualisiert
Matthew Flynn
4

Das Problem bei der Skalierung besteht darin, dass Code auf viele verschiedene Arten skaliert werden kann. Das Problem, das Sie haben, wird einfach durch die Notwendigkeit verursacht, in eine Richtung zu skalieren, für die das Entwurfsmuster nicht vorgesehen ist. Im Falle des Abstract Factory-Musters soll es so skaliert werden, dass neue Betonfabriken hinzugefügt werden. Das Hinzufügen neuer Produkte führt jedoch zu großen strukturellen Änderungen.

Einer der Punkte des Software-Designs und der Architektur besteht darin, die wahrscheinlichsten Richtungen zu identifizieren, in die der Code skaliert, und Muster basierend auf diesen wahrscheinlichen Richtungen auszuwählen. Wenn Sie feststellen, dass das Hinzufügen eines neuen Produkts wahrscheinlicher ist als das Hinzufügen einer neuen Betonfabrik, ist die Verwendung von Abstract Factory keine gute Idee, und es ist möglicherweise besser, das Design komplett zu überdenken.

Euphorisch
quelle
1
Ihre Antwort ist sinnvoller als die starre "ABSTRACT FACTORY SCALES FINE!" des anderen Mannes ...
Türkisch
@Euphoric, wie Sie vorschlagen, abstrakte Fabrikmusterwaage, um neue Betonfabriken hinzuzufügen, aber schließlich muss die neue Betonfabrik Produkte generieren, was auf die Produkterstellung hinausläuft und letztendlich sollten wir strukturelle Änderungen vornehmen, ist das nicht Recht?
Jitendar
@Jitendar Ich verstehe nicht, was du meinst. Der Punkt der abstrakten Fabrik ist, dass es eine relativ stabile Familie verwandter Abstraktionen (ProductA, ProductB) gibt und konkrete Fabriken Implementierungen dieser Abstraktionen erstellen, die ebenfalls irgendwie verwandt sind. Wenn die Möglichkeit, neue Abstraktionen hinzuzufügen, höher ist als das Hinzufügen neuer konkreter Implementierungen dieser Abstraktionen, ist die Verwendung von Abstract Factory keine gute Wahl.
Euphoric
3

Ja, Sie haben Recht, "abstrakte Fabrik" lässt sich nicht gut skalieren, wenn Sie zusätzliche abstrakte Produkte benötigen .

In vielen realen Szenarien müssen Sie jedoch eine sich ändernde Anzahl von Produkten, aber nur eine kleine, feste Anzahl von abstrakten Produkten unterstützen. Nehmen Sie zum Beispiel das Beispiel für GUI-Widgets aus dem Wikipedia-Artikel über abstrakte Fabriken . Die abstrakte GUI-Factory könnte eine Methode createWidget(anstelle von createButton) haben, bei der Widgetes sich um das "abstrakte Produkt" handelt, das die oberste Basisklasse für eine Familie von mehreren Dutzend GUI-Elementen darstellt. Das Hinzufügen neuer GUI-Widgets bedeutet in keiner Weise eine Änderung der Benutzeroberfläche der abstrakten Factory, sodass keine der Schnittstellen der konkreten Factory geändert werden muss.

Viele abstrakte Produkte bedeuten, dass Ihr Code viele verschiedene Klassenhierarchien enthält. In diesem Fall können Sie für jede Hierarchie eine eigene Factory (oder abstrakte Factory) erstellen.

Doc Brown
quelle
0

Ihr Skalierungsfaktor ist eine einfache Frage des Abhängigkeitsmanagements. Je mehr Abhängigkeiten eine Klasse hat, desto stabiler und durchdachter sollte ihre API sein.

Es ist kein Problem, eine Fabrik zu schaffen, die durch die Verantwortung für die Schaffung von Typen ähnlicher oder zusammenhängender Natur definiert ist. Aber je mehr Klassen von dieser Fabrik abhängen, desto strenger sollte dieser Zusammenhalt sein.

(Hinweis: Dies kann schrittweise erfolgen, nicht unbedingt durch einen BDUF.)

Yam Marcovic
quelle