Zu diesem Zeitpunkt sind Sie wahrscheinlich bereits auf Designmuster gestoßen. Zum Beispiel Singleton .

Erinnern wir uns daran, was Muster sind, warum sie benötigt werden und was Schöpfungsmuster sind (Singleton ist ein Beispiel). Wir werden auch ein neues Muster untersuchen: die Fabrikmethode.

In der Softwareentwicklung ist ein Entwurfsmuster ein wiederholbares Architekturkonstrukt, das eine Lösung für ein Entwurfsproblem in einem wiederkehrenden Kontext darstellt.

Normalerweise ist ein Muster keine endgültige Lösung, die direkt in Code umgewandelt werden kann. Es handelt sich lediglich um eine Musterlösung für ein Problem, die in verschiedenen Situationen eingesetzt werden kann.

Kreationsmuster sind Entwurfsmuster, die sich mit dem Prozess der Erstellung von Objekten befassen. Sie ermöglichen die Schaffung eines Systems, das unabhängig von der Methode zur Erstellung, Zusammenstellung und Präsentation von Objekten ist.

Eine Factory-Methode ist ein Erstellungsentwurfsmuster, das eine gemeinsame Schnittstelle zum Erstellen von Objekten in einer übergeordneten Klasse definiert und ihren Nachkommen die Möglichkeit gibt, diese Objekte zu erstellen. Zum Zeitpunkt der Erstellung können Nachkommen bestimmen, welche Klasse erstellt werden soll.

Welches Problem löst das Muster?

Stellen Sie sich vor, Sie beschließen, ein Lieferprogramm zu erstellen. Zunächst mieten Sie Kuriere mit Autos und nutzen einenAutoObjekt zur Darstellung eines Lieferfahrzeugs im Programm. Kuriere liefern Pakete von Punkt A nach Punkt B und so weiter. Kinderleicht.

Das Programm erfreut sich zunehmender Beliebtheit. Ihr Unternehmen wächst und Sie möchten in neue Märkte expandieren. Beispielsweise könnten Sie damit beginnen, auch Lebensmittel auszuliefern und Fracht zu versenden. In diesem Fall können Lebensmittel per Kurier zu Fuß, mit Rollern und Fahrrädern geliefert werden, für den Transport sind jedoch Lastkraftwagen erforderlich.

Jetzt müssen Sie den Überblick über verschiedene Dinge behalten (wann, an wen, was und wie viel geliefert wird), einschließlich der Menge, die jeder Kurier transportieren kann. Die neuen Transportmittel haben unterschiedliche Geschwindigkeiten und Kapazitäten. Dann stellen Sie fest, dass die meisten Entitäten in Ihrem Programm stark mit dem verknüpft sindAutoKlasse. Sie erkennen, dass Sie die vorhandene Codebasis neu schreiben müssen, damit Ihr Programm mit anderen Bereitstellungsmethoden funktioniert, und zwar jedes Mal, wenn Sie ein neues Fahrzeug hinzufügen.

Das Ergebnis ist schrecklicher Code voller bedingter Anweisungen, die je nach Transportart unterschiedliche Aktionen ausführen.

Die Lösung

Das Factory-Methodenmuster schlägt vor, Objekte durch den Aufruf einer speziellen Factory-Methode zu erstellen, anstatt direkt den neuen Operator zu verwenden. Unterklassen der Klasse, die über die Factory-Methode verfügt, können die erstellten Objekte der spezifischen Fahrzeuge ändern. Auf den ersten Blick mag das sinnlos erscheinen: Wir haben den Konstruktoraufruf einfach von einer Stelle im Programm an eine andere verschoben. Aber jetzt können Sie die Factory-Methode in einer Unterklasse überschreiben, um die Art des erstellten Transports zu ändern.

Schauen wir uns das Klassendiagramm für diesen Ansatz an:

Damit dieses System funktioniert, müssen alle zurückgegebenen Objekte über eine gemeinsame Schnittstelle verfügen. Unterklassen können Objekte verschiedener Klassen erzeugen, die diese Schnittstelle implementieren.

Beispielsweise implementieren die Klassen „Truck “ und „ Car“ die CourierTransport- Schnittstelle mit einer Deliver -Methode. Jede dieser Klassen implementiert die Methode auf unterschiedliche Weise: LKWs liefern Fracht, während Autos Lebensmittel, Pakete usw. liefern. Die Factory-Methode in der TruckCreator- Klasse gibt ein LKW-Objekt zurück und die CarCreator- Klasse gibt ein Pkw-Objekt zurück.

Für den Client der Factory-Methode gibt es keinen Unterschied zwischen diesen Objekten, da er sie als eine Art abstrakten CourierTransport behandelt . Dem Kunden wird es sehr am Herzen liegen, dass das Objekt über eine Methode zur Bereitstellung verfügt, aber wie genau diese Methode funktioniert, ist nicht wichtig.

Implementierung in Java:


public interface CourierTransport {
	void deliver();
}
public class Car implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The package is being delivered by car");
	}
}
public class Truck implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The freight is being delivered by truck");
	}
}
public abstract class CourierTransportCreator {
	public abstract CourierTransport createTransport();
}
public class CarCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Car();
	}
}
public class TruckCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Truck();
	}
}
 
public class Delivery {
	private String address;
	private CourierTransport courierTransport;
 
	public void Delivery() {
	}
 
	public Delivery(String address, CourierTransport courierTransport) {
    	this.address = address;
    	this.courierTransport = courierTransport;
	}
 
	public CourierTransport getCourierTransport() {
    		return courierTransport;
	}
 
	public void setCourierTransport(CourierTransport courierTransport) {
    		this.courierTransport = courierTransport;
	}
 
	public String getAddress() {
    		return address;
	}
 
	public void setAddress(String address) {
    		this.address = address;
	}
}
public static void main(String[] args) {
    	// Accept a new type of order from the database (pseudocode)
    	String type = database.getTypeOfDeliver();
 
    	Delivery delivery = new Delivery();
    	
    	// Set the transport for delivery
        delivery.setCourierTransport(getCourierTransportByType(type));
    	
    	// Make the delivery
        delivery.getCourierTransport().deliver();
 
	}
 
	public static CourierTransport getCourierTransportByType(String type) {
    	switch (type) {
        	case "CarDelivery":
            	return new CarCreator().createTransport();
        	case "TruckDelivery":
            	return new TruckCreator().createTransport();
        	default:
            	throw new RuntimeException();
	    }
	}
    

Wenn wir ein neues Lieferobjekt erstellen möchten, erstellt das Programm automatisch ein entsprechendes Transportobjekt.

Wann sollten wir dieses Muster anwenden?

1. Wenn Sie die Typen und Abhängigkeiten der Objekte, mit denen Ihr Code arbeiten muss, nicht im Voraus kennen.

Die Factory-Methode trennt den Code zur Herstellung von Transportmitteln von dem Code, der das Transportmittel nutzt. Dadurch kann der Code zum Erstellen von Objekten erweitert werden, ohne den Rest des Codes anzutasten.

Um beispielsweise Unterstützung für einen neuen Transporttyp hinzuzufügen, müssen Sie eine neue Unterklasse erstellen und darin eine Factory-Methode definieren, die eine Instanz des neuen Transports zurückgibt.

2. Wenn Sie Systemressourcen sparen möchten, indem Sie vorhandene Objekte wiederverwenden, anstatt neue zu erstellen.

Dieses Problem tritt normalerweise auf, wenn mit ressourcenintensiven Objekten wie Datenbankverbindungen, Dateisystemen usw. gearbeitet wird.

Überlegen Sie, welche Schritte Sie unternehmen müssen, um vorhandene Objekte wiederzuverwenden:

  1. Zunächst müssen Sie ein gemeinsames Repository erstellen, um alle von Ihnen erstellten Objekte zu speichern.

  2. Wenn Sie ein neues Objekt anfordern, müssen Sie im Repository nachsehen, ob es ein verfügbares Objekt enthält.

  3. Geben Sie das Objekt an den Clientcode zurück.

  4. Wenn jedoch keine Objekte verfügbar sind, erstellen Sie ein neues und fügen Sie es dem Repository hinzu.

Der gesamte Code muss an einem Ort abgelegt werden, der den Client-Code nicht überfüllt. Der bequemste Ort wäre der Konstruktor, da wir alle diese Prüfungen nur beim Erstellen von Objekten benötigen. Leider erstellt ein Konstruktor immer ein neues Objekt – er kann kein vorhandenes Objekt zurückgeben.

Das bedeutet, dass eine andere Methode benötigt wird, die sowohl bestehende als auch neue Objekte zurückgeben kann. Dies wird die Werksmethode sein.

3. Wenn Sie Benutzern erlauben möchten, Teile Ihres Frameworks oder Ihrer Bibliothek zu erweitern.

Benutzer können Ihre Framework-Klassen durch Vererbung erweitern. Aber wie bringt man das Framework dazu, Objekte dieser neuen Klassen anstelle der Standardklassen zu erstellen?

Die Lösung besteht darin, Benutzern nicht nur die Erweiterung der Komponenten, sondern auch der Klassen, die diese Komponenten erstellen, zu ermöglichen. Und dafür müssen die erstellenden Klassen über spezifische Erstellungsmethoden verfügen, die definiert werden können.

Vorteile

  • Entkoppelt eine Klasse von bestimmten Transportklassen.
  • Bewahrt den Code zum Erstellen von Transportmitteln an einem Ort auf, wodurch der Code leichter verwaltet werden kann.
  • Vereinfacht das Hinzufügen neuer Transportmittel zum Programm.
  • Implementiert das Offen-Geschlossen-Prinzip.

Nachteile

Kann zu großen parallelen Klassenhierarchien führen, da jede Produktklasse ihre eigene Ersteller-Unterklasse haben muss.

Fassen wir zusammen

Sie haben das Factory-Methodenmuster kennengelernt und eine mögliche Implementierung gesehen. Dieses Muster wird häufig in verschiedenen Bibliotheken verwendet, die Objekte zum Erstellen anderer Objekte bereitstellen.

Verwenden Sie das Factory-Methodenmuster, wenn Sie einfach neue Objekte von Unterklassen vorhandener Klassen hinzufügen möchten, um mit Ihrer Hauptgeschäftslogik zu interagieren und Ihren Code nicht aufgrund unterschiedlicher Kontexte aufzublähen.