På dette tidspunktet har du sannsynligvis allerede møtt designmønstre. For eksempel singleton .

La oss huske hva mønstre er, hvorfor de trengs, og hva kreasjonsmønstre er (singleton er et eksempel). Vi skal også studere et nytt mønster: fabrikkmetoden.

I programvareutvikling er et designmønster en repeterbar arkitektonisk konstruksjon som representerer en løsning på et designproblem innenfor en tilbakevendende kontekst.

Vanligvis er ikke et mønster en endelig løsning som kan konverteres direkte til kode. Det er bare en modellløsning på et problem som kan brukes i ulike situasjoner.

Kreasjonelle mønstre er designmønstre som omhandler prosessen med å lage objekter. De gjør det mulig å lage et system som er uavhengig av metoden som brukes for å lage, komponere og presentere objekter.

En fabrikkmetode er et kreativt designmønster som definerer et felles grensesnitt for å lage objekter i en overordnet klasse, og gir dens etterkommere muligheten til å lage disse objektene. På opprettelsestidspunktet kan etterkommere bestemme hvilken klasse som skal opprettes.

Hvilket problem løser mønsteret?

Tenk deg at du bestemmer deg for å lage et leveringsprogram. I utgangspunktet vil du leie kurerer med biler og bruke enBilobjekt for å representere et leveringskjøretøy i programmet. Bud leverer pakker fra punkt A til punkt B og så videre. Enkel peasy.

Programmet blir stadig mer populært. Bedriften din vokser og du ønsker å ekspandere til nye markeder. For eksempel kan du også begynne å levere mat og frakt. I dette tilfellet kan mat leveres med bud til fots, på scootere og på sykler, men lastebiler er nødvendig for frakt.

Nå må du holde styr på flere ting (når, til hvem, hva og hvor mye som skal leveres), inkludert hvor mye hver kurer kan bære. De nye transportformene har ulik hastighet og kapasitet. Da legger du merke til at de fleste enhetene i programmet ditt er sterkt knyttet tilBilklasse. Du innser at for å få programmet til å fungere med andre leveringsmetoder, må du skrive om den eksisterende kodebasen og gjøre det igjen hver gang du legger til et nytt kjøretøy.

Resultatet er fryktelig kode fylt med betingede utsagn som utfører forskjellige handlinger avhengig av type transport.

Løsningen

Fabrikkmetodemønsteret foreslår å lage objekter ved å kalle en spesiell fabrikkmetode i stedet for å bruke den nye operatøren direkte. Underklasser av klassen som har fabrikkmetoden kan endre de opprettede objektene til de spesifikke kjøretøyene. Ved første øyekast kan dette virke meningsløst: vi har ganske enkelt flyttet konstruktøranropet fra ett sted i programmet til et annet. Men nå kan du overstyre fabrikkmetoden i en underklasse for å endre typen transport som opprettes.

La oss se på klassediagrammet for denne tilnærmingen:

For at dette systemet skal fungere, må alle returnerte objekter ha et felles grensesnitt. Underklasser vil kunne produsere objekter av forskjellige klasser som implementerer dette grensesnittet.

For eksempel implementerer lastebil- og bilklassene CourierTransport- grensesnittet med en leveringsmetode . Hver av disse klassene implementerer metoden på en annen måte: lastebiler leverer frakt, mens biler leverer mat, pakker og så videre. Fabrikkmetoden i TruckCreator- klassen returnerer et lastebilobjekt, og CarCreator- klassen returnerer et bilobjekt.

For klienten av fabrikkmetoden er det ingen forskjell mellom disse objektene, siden den vil behandle dem som en slags abstrakt CourierTransport . Klienten vil bry seg om at objektet har en metode for å levere, men hvordan akkurat den metoden fungerer er ikke viktig.

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

Hvis vi ønsker å lage et nytt leveringsobjekt, så lager programmet automatisk et passende transportobjekt.

Når bør vi bruke dette mønsteret?

1. Når du ikke på forhånd vet hvilke typer og avhengigheter av objektene som koden din må jobbe med.

Fabrikkmetoden skiller koden for å produsere transportformer fra koden som bruker transporten. Som et resultat kan koden for å lage objekter utvides uten å berøre resten av koden.

For å legge til støtte for en ny type transport, må du for eksempel opprette en ny underklasse og definere en fabrikkmetode i den som returnerer en forekomst av den nye transporten.

2. Når du vil spare systemressurser ved å gjenbruke eksisterende objekter i stedet for å lage nye.

Dette problemet oppstår vanligvis når du arbeider med ressurskrevende objekter, for eksempel databasetilkoblinger, filsystemer osv.

Tenk på trinnene du må ta for å gjenbruke eksisterende objekter:

  1. Først må du opprette et delt depot for å lagre alle objektene du oppretter.

  2. Når du ber om et nytt objekt, må du se i depotet og sjekke om det inneholder et tilgjengelig objekt.

  3. Returner objektet til klientkoden.

  4. Men hvis det ikke er noen tilgjengelige objekter, oppretter du et nytt og legger det til i depotet.

All denne koden må plasseres et sted som ikke roter opp klientkoden. Det mest praktiske stedet ville være konstruktøren, siden vi bare trenger alle disse sjekkene når vi lager objekter. Dessverre, en konstruktør oppretter alltid et nytt objekt - den kan ikke returnere et eksisterende objekt.

Det betyr at det trengs en annen metode som kan returnere både eksisterende og nye objekter. Dette vil være fabrikkmetoden.

3. Når du vil tillate brukere å utvide deler av rammeverket eller biblioteket ditt.

Brukere kan utvide rammeklassene dine gjennom arv. Men hvordan får du rammeverket til å lage objekter av disse nye klassene i stedet for standardklassene?

Løsningen er å la brukere utvide ikke bare komponentene, men også klassene som lager disse komponentene. Og for dette må opprettelsesklassene ha spesifikke opprettelsesmetoder som kan defineres.

Fordeler

  • Kobler en klasse fra spesifikke transportklasser.
  • Holder koden for å lage transportformer på ett sted, noe som gjør koden enklere å vedlikeholde.
  • Forenkler tillegget av nye transportmåter til programmet.
  • Implementerer åpent-lukket-prinsippet.

Ulemper

Kan føre til store parallelle klassehierarkier, siden hver produktklasse må ha sin egen skaperunderklasse.

La oss oppsummere

Du lærte om fabrikkmetodemønsteret og så en mulig implementering. Dette mønsteret brukes ofte i ulike biblioteker som tilbyr objekter for å lage andre objekter.

Bruk fabrikkmetodemønsteret når du enkelt vil legge til nye objekter av underklasser av eksisterende klasser for å samhandle med hovedforretningslogikken din og ikke blåse opp koden din på grunn av forskjellige kontekster.