Wann ist es eine gute Idee, Factory-Methoden innerhalb eines Objekts anstelle einer Factory-Klasse zu verwenden?
Wann ist es eine gute Idee, Factory-Methoden innerhalb eines Objekts anstelle einer Factory-Klasse zu verwenden?
Antworten:
Ich denke gerne über Designmuster nach, wenn meine Klassen „Menschen“ sind, und die Muster sind die Art und Weise, wie die Menschen miteinander sprechen.
Für mich ist das Fabrikmuster also wie eine Personalagentur. Sie haben jemanden, der eine variable Anzahl von Arbeitern benötigt. Diese Person kennt möglicherweise einige Informationen, die sie für die von ihnen eingestellten Personen benötigt, aber das war's.
Wenn sie also einen neuen Mitarbeiter benötigen, rufen sie die Personalagentur an und sagen ihnen, was sie brauchen. Um tatsächlich jemanden einstellen zu können , müssen Sie eine Menge Dinge wissen - Vorteile, Überprüfung der Berechtigung usw. Aber die Person, die anstellt, muss nichts davon wissen - die Einstellungsagentur kümmert sich um all das.
Auf die gleiche Weise ermöglicht die Verwendung einer Factory dem Verbraucher, neue Objekte zu erstellen, ohne die Details zu kennen, wie sie erstellt wurden oder welche Abhängigkeiten sie haben - sie müssen nur die Informationen angeben, die sie tatsächlich möchten.
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 Verbraucher der ThingFactory eine Sache erhalten, ohne über die Abhängigkeiten der Sache Bescheid wissen zu müssen, mit Ausnahme der Zeichenfolgendaten, die vom Verbraucher stammen.
within an object instead of a Factory class
. Ich denke, er meinte das Szenario, in dem Sie den ctor privat machen und eine statische Methode verwenden, um die Klasse zu instanziieren (ein Objekt erstellen). ThingFactory
Um diesem Beispiel zu folgen, muss man zuerst die Klasse instanziieren , um Thing
Objekte zu erhalten , was dies zu einem Factory class
Effekt macht.
Factory-Methoden sollten als Alternative zu Konstruktoren betrachtet werden - meistens, wenn Konstruktoren nicht ausdrucksstark genug sind, d. H.
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 tatsächliche Klasse nicht benötigen, wenn Sie verschiedene Objekte usw. erstellen müssen.
Eine Situation, in der ich persönlich separate Factory-Klassen für sinnvoll halte, besteht darin, dass das endgültige Objekt, das Sie erstellen möchten, von mehreren anderen Objekten abhängt. Beispiel: In PHP: Angenommen, Sie haben ein House
Objekt, das wiederum ein Kitchen
und ein LivingRoom
Objekt enthält, und das LivingRoom
Objekt enthält auch 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, werden Sie beim House
fehlgeschlagenen Erstellen wahrscheinlich einige Zeit damit verbringen, genau zu isolieren, was fehlschlägt.
Die Alternative besteht darin, Folgendes zu tun (Abhängigkeitsinjektion, wenn Sie den ausgefallenen Begriff mögen):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Wenn der Prozess des Erstellens eines House
fehlschlägt, gibt es nur einen Ort, an dem Sie suchen müssen. Es ist jedoch alles andere als House
praktisch, diesen Block jedes Mal verwenden zu müssen, wenn Sie einen neuen wünschen . Betreten Sie die Fabriken:
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 wird der Prozess des Erstellens eines a House
abstrahiert (da Sie nicht jede einzelne Abhängigkeit erstellen und einrichten müssen, wenn Sie nur ein erstellen möchten House
) und gleichzeitig zentralisiert, was die Wartung erleichtert. Es gibt andere Gründe, warum die Verwendung separater Fabriken von Vorteil sein kann (z. B. Testbarkeit), aber ich finde diesen speziellen Anwendungsfall, um am besten zu veranschaulichen, wie nützlich Factory-Klassen sein können.
HouseFactory
Klasse übergeben?
create
Methode. Wenn Sie House
beispielsweise immer die gleiche Art von LivingRoom
haben, kann es sinnvoll sein, die Parameter in der Factory-Klasse fest zu codieren, anstatt sie als Argumente zu übergeben. Oder Sie möchten type
Ihrer HouseFactory::create
Methode ein Argument geben, wenn Sie einige Arten von LivingRoom
s haben und einen Schalter mit den für jeden Typ fest codierten Parametern haben.
Es ist wichtig, die Idee hinter der Verwendung der Fabrik oder der Fabrikmethode klar zu unterscheiden. Beide sollen sich gegenseitig ausschließende Probleme bei der Objekterstellung ausschließen.
Lassen Sie uns die "Fabrikmethode" genau beschreiben:
Wenn Sie eine Bibliothek oder APIs entwickeln, die wiederum für die weitere Anwendungsentwicklung verwendet werden, ist die Factory-Methode eine der besten Auswahlmöglichkeiten für das Erstellungsmuster. Grund dahinter; Wir wissen, dass, wann ein Objekt mit den erforderlichen Funktionen erstellt werden soll, der Objekttyp jedoch unentschlossen bleibt oder ob dynamische Parameter übergeben werden .
Nun ist der Punkt, dass ungefähr dasselbe erreicht werden kann, indem das Factory-Muster selbst verwendet wird, aber ein großer Nachteil wird in das System eingeführt, wenn das Factory-Muster für das oben hervorgehobene Problem verwendet wird. Es ist, dass Ihre Logik, verschiedene Objekte (Unterklassenobjekte) zu erstellen, dies tut Seien Sie in Zukunft spezifisch für bestimmte Geschäftsbedingungen, wenn Sie die Funktionalität Ihrer Bibliothek für andere Plattformen erweitern müssen (Technisch gesehen müssen Sie weitere Unterklassen der Basisschnittstelle oder der abstrakten Klasse hinzufügen, damit Factory diese Objekte zusätzlich zu den vorhandenen auch zurückgibt basierend auf einigen dynamischen Parametern) dann jedes Mal, wenn Sie die Logik der Factory-Klasse ändern (erweitern) müssen, was kostspielig und aus konstruktiver Sicht nicht gut ist. Auf der anderen Seite, wenn "Fabrikmethode"
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();
}
}
Es ist eine gute Idee, Factory-Methoden innerhalb des Objekts zu verwenden, wenn:
Es ist eine gute Idee, eine abstrakte Factory- Klasse zu verwenden, wenn:
UML von
Produkt: Definiert eine Schnittstelle der Objekte, die die Factory-Methode erstellt.
ConcreteProduct: Implementiert die Produktschnittstelle
Ersteller: Deklariert die Factory-Methode
ConcreateCreator: Implementiert die Factory-Methode, um eine Instanz eines ConcreteProduct zurückzugeben
Problemstellung: Erstellen Sie eine Factory of Games mithilfe von Factory-Methoden, die die Spieloberfläche definieren.
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 durch Implementierung von a FactoryMethod
.
Game
ist die Schnittstelle für alle Arten von Spielen. Es definiert komplexe Methoden:createGame()
Chess, Ludo, Checkers
Es gibt verschiedene Varianten von Spielen, die eine Implementierung ermöglichen createGame()
public Game getGame(String gameName)
ist FactoryMethod
in der IGameFactory
Klasse
GameFactory
Erstellt verschiedene Arten von Spielen im Konstruktor vor. Es implementiert die IGameFactory
Factory-Methode.
Der Spielname wird als Befehlszeilenargument an übergeben NotStaticFactoryDemo
getGame
in GameFactory
akzeptiert einen Spielnamen und gibt das entsprechende Game
Objekt zurück.
Fabrik:
Erstellt Objekte, ohne die Instanziierungslogik dem Client zur Verfügung zu stellen.
FactoryMethod
Definieren Sie eine Schnittstelle zum Erstellen eines Objekts, lassen Sie jedoch die Unterklassen entscheiden, welche Klasse instanziiert werden soll. Mit der Factory-Methode kann eine Klasse die Instanziierung auf Unterklassen verschieben
Anwendungsfall:
Wann zu verwenden: Client
weiß nicht, welche konkreten Klassen zur Laufzeit erstellt werden müssen, sondern möchte nur eine Klasse erhalten, die den Job erledigt.
getArea()
ist keine Factory - Methode überhaupt .
Es ist wirklich Geschmackssache. Factory-Klassen können nach Bedarf abstrahiert / verbunden werden, während Factory-Methoden leichter sind (und auch testbar sind, da sie keinen definierten Typ haben, aber einen bekannten Registrierungspunkt erfordern, der einem Service ähnelt Locator, aber zum Auffinden von Werksmethoden).
Factory-Klassen sind nützlich, wenn der von ihnen zurückgegebene Objekttyp einen privaten Konstruktor hat, wenn verschiedene Factory-Klassen unterschiedliche Eigenschaften für das zurückgebende Objekt festlegen oder wenn ein bestimmter Factory-Typ mit seinem zurückkehrenden Betontyp gekoppelt ist.
WCF verwendet ServiceHostFactory-Klassen, um ServiceHost-Objekte in verschiedenen Situationen abzurufen. Die Standard-ServiceHostFactory wird von IIS zum Abrufen von ServiceHost-Instanzen für SVC- Dateien verwendet. Eine WebScriptServiceHostFactory wird jedoch für Dienste verwendet, die Serialisierungen an JavaScript-Clients zurückgeben. ADO.NET Data Services verfügt über eine eigene spezielle DataServiceHostFactory und ASP.NET über eine ApplicationServicesHostFactory, da die 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.
Stellen Sie sich ein Szenario vor, in dem Sie eine Auftrags- und Kundenklasse entwerfen müssen. Der Einfachheit halber und den anfänglichen Anforderungen zufolge benötigen Sie für die Auftragsklasse keine Fabrik und füllen Ihre Anwendung mit vielen 'new Order ()' - Anweisungen. Die Dinge funktionieren gut.
Nun kommt eine neue Anforderung ins Spiel, dass das Bestellobjekt ohne Kundenzuordnung nicht instanziiert werden kann (neue Abhängigkeit). Jetzt haben Sie folgende Überlegungen.
1- Sie erstellen eine Konstruktorüberladung, die nur für neue Implementierungen funktioniert. (Inakzeptabel). 2- Sie ändern die Order () - Signaturen und ändern jeden Aufruf. (Keine gute Übung und echte Schmerzen).
Stattdessen Wenn Sie eine Factory für die Auftragsklasse erstellt haben, müssen Sie nur eine Codezeile ändern, und schon kann es losgehen. Ich empfehle die Factory-Klasse für fast jede aggregierte Assoziation. Hoffentlich hilft das.
wenn Sie ein anderes Objekt in Bezug auf die Verwendung 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;
}
}
Jede Klasse, die die Objekterstellung für das Objekt, mit dem sie arbeiten muss, auf ihre Unterklasse verschiebt, kann als Beispiel für ein Factory-Muster angesehen werden.
Ich habe dies in einer anderen Antwort unter https://stackoverflow.com/a/49110001/504133 ausführlich erwähnt
Ich denke, es hängt vom losen Kopplungsgrad ab, den Sie in Ihren Code einbringen möchten.
Die Fabrikmethode entkoppelt die Dinge sehr gut, aber die Fabrikklasse Nr.
Mit anderen Worten, es ist einfacher, Dinge zu ändern, wenn Sie die Factory-Methode verwenden, als wenn Sie eine einfache Factory (als Factory-Klasse bezeichnet) verwenden.
Schauen Sie sich dieses Beispiel an: https://connected2know.com/programming/java-factory-pattern/ . Stellen Sie sich nun vor, Sie möchten ein neues Tier mitbringen. In der Factory-Klasse müssen Sie die Factory ändern, aber in der Factory-Methode müssen Sie nur eine neue Unterklasse hinzufügen.
Fabrikklassen sind schwerer, bieten aber gewisse Vorteile. In Fällen, in denen Sie Ihre Objekte aus mehreren Rohdatenquellen erstellen müssen, können Sie nur die Gebäudelogik (und möglicherweise die Aggregation der Daten) an einem Ort 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 unzureichend bin und viele Objekte aus DB-Tabellenverknüpfungen oder gespeicherten Prozeduren effizient instanziieren möchte.
Ich vergleiche Fabriken mit dem Konzept der Bibliotheken. Beispielsweise können Sie eine Bibliothek zum Arbeiten mit Zahlen und eine andere zum Arbeiten mit Formen haben. Sie können die Funktionen dieser Bibliotheken in logisch benannten Verzeichnissen als Numbers
oder speichern Shapes
. Dies sind generische Typen, die bei Formen Ganzzahlen, Gleitkommazahlen, Dobuli, Longs oder Rechtecke, Kreise, Dreiecke und Pentagone umfassen können.
Der Factory Petter verwendet Polymorphismus, Abhängigkeitsinjektion 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 also an, Sie erstellen ein Betriebssystem oder Framework und alle diskreten Komponenten.
Hier ist ein einfaches Beispiel für das Konzept des Factory-Musters in PHP. Ich bin vielleicht nicht zu 100% damit einverstanden, 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 );
NumbersFactory::makeNumber( 'float', 12.5 );
es mir zu sagen, new Float(12.5);
wenn ich weiß, dass ich eine brauche Float
? Das verstehe ich nicht über Fabriken ... worum geht es?