CodeGym /Java-blogg /Tilfeldig /Designmønstre: Abstrakt fabrikk
John Squirrels
Nivå
San Francisco

Designmønstre: Abstrakt fabrikk

Publisert i gruppen
Hei! I dag vil vi fortsette å studere designmønstre, og vi vil diskutere det abstrakte fabrikkmønsteret . Designmønstre: Abstrakt fabrikk - 1Her er hva vi skal dekke i leksjonen:
  • Vi vil diskutere hva en abstrakt fabrikk er og hvilket problem dette mønsteret løser
  • Vi vil lage skjelettet til en tverrplattformapplikasjon for bestilling av kaffe gjennom et brukergrensesnitt
  • Vi vil studere instruksjoner om hvordan du bruker dette mønsteret, inkludert å se på et diagram og kode
  • Og som en bonus inkluderer denne leksjonen et skjult påskeegg som vil hjelpe deg å lære hvordan du bruker Java til å bestemme navnet på operativsystemet og, avhengig av resultatet, utføre en annen handling eller en annen.
For å forstå dette mønsteret fullt ut, må du være godt kjent med følgende emner:
  • arv i Java
  • abstrakte klasser og metoder i Java

Hvilke problemer løser en abstrakt fabrikk?

En abstrakt fabrikk, som alle fabrikkmønstre, hjelper oss med å sikre at nye objekter lages riktig. Vi bruker den til å administrere "produksjonen" av ulike familier av sammenkoblede objekter. Ulike familier av sammenkoblede objekter... Hva betyr det? Ikke bekymre deg: i praksis er alt enklere enn det kan virke. Til å begynne med, hva kan en familie av sammenkoblede objekter være? Anta at vi utvikler en militær strategi som involverer flere typer enheter:
  • infanteri
  • kavaleri
  • bueskyttere
Disse typene enheter er sammenkoblet, fordi de tjener i samme hær. Vi kan si at kategoriene oppført ovenfor er en familie av sammenkoblede objekter. Vi forstår dette. Men det abstrakte fabrikkmønsteret brukes til å arrangere opprettelsen av forskjellige familier av sammenkoblede objekter. Det er ikke noe komplisert her heller. La oss fortsette med militærstrategieksemplet. Generelt sett tilhører militære enheter flere forskjellige stridende parter. Avhengig av hvem sin side de er på, kan militære enheter variere betydelig i utseende. Den romerske hærens fotsoldater, ryttere og bueskyttere er ikke de samme som vikingfotsoldater, ryttere og bueskyttere. I den militære strategien er soldater fra forskjellige hærer forskjellige familier av sammenkoblede objekter. Det ville vært morsomt om en programmerer s feil førte til at en soldat i en fransk uniform fra Napoleon-tiden, med muskett klar, ble funnet gående blant rekkene til det romerske infanteriet. Det abstrakte fabrikkdesignmønsteret er nødvendig nettopp for å løse dette problemet. Nei, ikke problemet med flauheten som kan komme fra tidsreiser, men problemet med å skape ulike grupper av sammenkoblede objekter. En abstrakt fabrikk gir et grensesnitt for å lage alle tilgjengelige produkter (en familie av objekter). En abstrakt fabrikk har vanligvis flere implementeringer. Hver av dem er ansvarlig for å lage produkter fra en av familiene. Vår militærstrategi vil inkludere en abstrakt fabrikk som skaper abstrakte fotsoldater, bueskyttere og kavalerister, samt implementeringer av denne fabrikken. For eksempel, en fabrikk som lager romerske legionærer og en fabrikk som lager karthagiske soldater. Abstraksjon er dette mønsterets viktigste ledende prinsipp. Fabrikkens kunder arbeider med fabrikken og dens produkter kun gjennom abstrakte grensesnitt. Som et resultat trenger du ikke tenke på hvilke soldater som for tiden opprettes. I stedet overfører du dette ansvaret til en konkret implementering av den abstrakte fabrikken.

La oss fortsette å automatisere kaffebaren vår

I siste leksjon, studerte vi fabrikkmetodemønsteret. Vi brukte den til å utvide kaffevirksomheten vår og åpne flere nye lokasjoner. I dag vil vi fortsette å modernisere virksomheten vår. Ved å bruke det abstrakte fabrikkmønsteret vil vi legge grunnlaget for en ny skrivebordsapplikasjon for bestilling av kaffe på nett. Når du skriver en skrivebordsapplikasjon, bør vi alltid tenke på støtte på tvers av plattformer. Vår applikasjon må fungere på både macOS og Windows (spoiler: Støtte for Linux er igjen for deg å implementere som lekser). Hvordan vil søknaden vår se ut? Ganske enkelt: det vil være et skjema som består av et tekstfelt, et utvalgsfelt og en knapp. Hvis du har erfaring med å bruke forskjellige operativsystemer, har du sikkert lagt merke til at knapper på Windows gjengis annerledes enn på en Mac. Som alt annet... Vel, la oss begynne.
  • knapper
  • tekstfelt
  • utvalgsfelt
Ansvarsfraskrivelse: I hvert grensesnitt kan vi definere metoder som onClick, onValueChanged, eller onInputChanged. Med andre ord kan vi definere metoder som lar oss håndtere ulike hendelser (trykke på en knapp, skrive inn tekst, velge en verdi i en valgboks). Alt dette er bevisst utelatt her for ikke å overbelaste eksemplet og for å gjøre det klarere når vi studerer fabrikkmønsteret. La oss definere abstrakte grensesnitt for produktene våre:

public interface Button {}
public interface Select {}
public interface TextField {}
For hvert operativsystem må vi lage grensesnittelementer i samme stil som operativsystemet. Vi skal skrive kode for Windows og MacOS. La oss lage implementeringer for Windows:

public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Nå gjør vi det samme for MacOS:

public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Utmerket. Nå kan vi fortsette til vår abstrakte fabrikk, som vil lage alle tilgjengelige abstrakte produkttyper:

public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
Fantastisk. Som du kan se, har vi ikke gjort noe komplisert ennå. Alt som følger er også enkelt. I analogi med produktene lager vi forskjellige fabrikkimplementeringer for hvert OS. La oss starte med Windows:

public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
Vi har lagt til litt konsollutgang i metodene og konstruktøren for å illustrere hva som skjer ytterligere. Nå for macOS:

public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
Merk at hver metodesignatur indikerer at metoden returnerer en abstrakt type. Men inne i metodene lager vi spesifikke implementeringer av produktene. Dette er det eneste stedet vi kontrollerer opprettelsen av spesifikke forekomster. Nå er det på tide å skrive en klasse for skjemaet. Dette er en Java-klasse hvis felt er grensesnittelementer:

public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
En abstrakt fabrikk som lager grensesnittelementer sendes til skjemaets konstruktør. Vi vil sende den nødvendige fabrikkimplementeringen til konstruktøren for å lage grensesnittelementer for et bestemt OS.

public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
Hvis vi kjører applikasjonen på Windows, får vi følgende utgang:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
På en Mac vil utgangen være som følger:

Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
På Linux:

Unknown OS. Unable to draw form :( 
Og nå oppsummerer vi. Vi skrev skjelettet til en GUI-basert applikasjon der grensesnittelementene er laget spesifikt for det aktuelle operativsystemet. Vi vil kort gjenta det vi har laget:
  • En produktfamilie som består av et inndatafelt, et utvalgsfelt og en knapp.
  • Ulike implementeringer av produktfamilien for Windows og macOS.
  • En abstrakt fabrikk som definerer et grensesnitt for å lage produktene våre.
  • To implementeringer av fabrikken vår, hver ansvarlig for å lage en bestemt familie av produkter.
  • Et skjema (en Java-klasse) hvis felt er abstrakte grensesnittelementer som initialiseres med de nødvendige verdiene i konstruktøren ved hjelp av en abstrakt fabrikk.
  • Applikasjonsklasse Inne i denne klassen lager vi et skjema som sender ønsket fabrikkimplementering til konstruktøren.
Resultatet er at vi implementerte det abstrakte fabrikkmønsteret.

Abstrakt fabrikk: hvordan du bruker

En abstrakt fabrikk er et designmønster for å administrere opprettelsen av ulike produktfamilier uten å være bundet til konkrete produktklasser. Når du bruker dette mønsteret, må du:
  1. Definer produktfamilier. Anta at vi har to av dem:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. For hvert produkt i familien, definer en abstrakt klasse (grensesnitt). I vårt tilfelle har vi:
    • ProductA
    • ProductB
  3. Innenfor hver produktfamilie må hvert produkt implementere grensesnittet definert i trinn 2.
  4. Lag en abstrakt fabrikk, med metoder for å lage hvert produkt definert i trinn 2. I vårt tilfelle vil disse metodene være:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Lag abstrakte fabrikkimplementeringer slik at hver implementering kontrollerer opprettelsen av produkter fra en enkelt familie. For å gjøre dette, inne i hver implementering av den abstrakte fabrikken, må du implementere alle kreasjonsmetoder slik at de oppretter og returnerer spesifikke produktimplementeringer.
Følgende UML-diagram illustrerer instruksjonene som er skissert ovenfor: Designmønstre: Abstrakt fabrikk - 3Nå skal vi skrive kode i henhold til disse instruksjonene:

    // Define common product interfaces
    public interface ProductA {}
    public interface ProductB {}

    // Create various implementations (families) of our products
    public class SpecificProductA1 implements ProductA {}
    public class SpecificProductB1 implements ProductB {}

    public class SpecificProductA2 implements ProductA {}
    public class SpecificProductB2 implements ProductB {}

    // Create an abstract factory
    public interface AbstractFactory {
        ProductA createProductA();
        ProductB createProductB();
    }

    // Implement the abstract factory in order to create products in family 1
    public class SpecificFactory1 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA1();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB1();
        }
    }

    // Implement the abstract factory in order to create products in family 2
    public class SpecificFactory2 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA2();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB2();
        }
    }

Hjemmelekser

For å forsterke materialet kan du gjøre to ting:
  1. Avgrens kaffebestillingsapplikasjonen slik at den også fungerer på Linux.
  2. Lag din egen abstrakte fabrikk for å produsere enheter involvert i enhver militær strategi. Dette kan enten være en historisk militærstrategi som involverer ekte hærer, eller en fantasistrategi med orker, nisser og alver. Det viktigste er å velge noe som interesserer deg. Vær kreativ, skriv ut meldinger på konsollen, og nyt å lære om mønstre!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION