CodeGym/Java blogg/Slumpmässig/Designmönster: Abstrakt fabrik
John Squirrels
Nivå
San Francisco

Designmönster: Abstrakt fabrik

Publicerad i gruppen
Hej! Idag kommer vi att fortsätta att studera designmönster och vi kommer att diskutera det abstrakta fabriksmönstret . Designmönster: Abstrakt fabrik - 1Här är vad vi kommer att ta upp i lektionen:
  • Vi kommer att diskutera vad en abstrakt fabrik är och vilket problem detta mönster löser
  • Vi kommer att skapa skelettet av en plattformsoberoende applikation för att beställa kaffe genom ett användargränssnitt
  • Vi kommer att studera instruktioner om hur man använder det här mönstret, inklusive att titta på ett diagram och en kod
  • Och som en bonus innehåller den här lektionen ett dolt påskägg som hjälper dig att lära dig hur du använder Java för att bestämma namnet på operativsystemet och, beroende på resultatet, utföra en eller annan åtgärd.
För att helt förstå detta mönster måste du vara väl insatt i följande ämnen:
  • arv i Java
  • abstrakta klasser och metoder i Java

Vilka problem löser en abstrakt fabrik?

En abstrakt fabrik, som alla fabriksmönster, hjälper oss att säkerställa att nya objekt skapas på rätt sätt. Vi använder den för att hantera "produktionen" av olika familjer av sammanlänkade objekt. Olika familjer av sammanlänkade föremål... Vad betyder det? Oroa dig inte: i praktiken är allt enklare än det kan verka. Till att börja med, vad kan en familj av sammankopplade objekt vara? Anta att vi utvecklar en militär strategi som involverar flera typer av enheter:
  • infanteri
  • kavalleri
  • bågskyttar
Dessa typer av enheter är sammankopplade, eftersom de tjänstgör i samma armé. Vi skulle kunna säga att kategorierna ovan är en familj av sammanlänkade objekt. Vi förstår detta. Men det abstrakta fabriksmönstret används för att arrangera skapandet av olika familjer av sammanlänkade föremål. Det är inget komplicerat här heller. Låt oss fortsätta med det militära strategiexemplet. Generellt sett tillhör militära enheter flera olika stridande parter. Beroende på vems sida de står på kan militära enheter variera avsevärt i utseende. Den romerska arméns fotsoldater, ryttare och bågskyttar är inte samma sak som vikingatida fotsoldater, ryttare och bågskyttar. I den militära strategin är soldater från olika arméer olika familjer av sammanlänkade föremål. Det skulle vara roligt om en programmerare s misstag gjorde att en soldat i en fransk uniform från Napoleon-tiden, med musköt redo, hittades gående bland det romerska infanteriet. Det abstrakta fabriksdesignmönstret behövs just för att lösa detta problem. Nej, inte problemet med pinsamheten som kan komma från tidsresor, utan problemet med att skapa olika grupper av sammanlänkade objekt. En abstrakt fabrik tillhandahåller ett gränssnitt för att skapa alla tillgängliga produkter (en familj av objekt). En abstrakt fabrik har vanligtvis flera implementeringar. Var och en av dem är ansvarig för att skapa produkter från en av familjerna. Vår militära strategi skulle innefatta en abstrakt fabrik som skapar abstrakta fotsoldater, bågskyttar och kavallerister, såväl som implementeringar av denna fabrik. Till exempel, en fabrik som skapar romerska legionärer och en fabrik som skapar karthagiska soldater. Abstraktion är detta mönsters viktigaste ledstjärna. Fabrikens kunder arbetar med fabriken och dess produkter endast genom abstrakta gränssnitt. Som ett resultat behöver du inte tänka på vilka soldater som för närvarande skapas. Istället överför du detta ansvar till någon konkret implementering av den abstrakta fabriken.

Låt oss fortsätta att automatisera vårt kafé

I sista lektionen, studerade vi fabriksmetodens mönster. Vi använde den för att utöka vår kaffeverksamhet och öppna flera nya platser. Idag fortsätter vi att modernisera vår verksamhet. Med hjälp av det abstrakta fabriksmönstret kommer vi att lägga grunden för en ny skrivbordsapplikation för att beställa kaffe online. När vi skriver en skrivbordsapplikation bör vi alltid tänka på plattformsoberoende stöd. Vår applikation måste fungera på både macOS och Windows (spoiler: Support för Linux är kvar för dig att implementera som läxa). Hur kommer vår ansökan att se ut? Ganska enkelt: det blir ett formulär som består av ett textfält, ett urvalsfält och en knapp. Om du har erfarenhet av att använda olika operativsystem har du säkert märkt att knappar på Windows renderas annorlunda än på en Mac. Precis som allt annat... Nåväl, låt oss börja.
  • knappar
  • textfält
  • urvalsfält
Friskrivningsklausul: I varje gränssnitt kan vi definiera metoder som , onClick, onValueChangedeller onInputChanged. Med andra ord kan vi definiera metoder som gör att vi kan hantera olika händelser (trycka på en knapp, skriva in text, välja ett värde i en urvalsruta). Allt detta är medvetet utelämnat här för att inte överbelasta exemplet och för att göra det tydligare när vi studerar fabriksmönstret. Låt oss definiera abstrakta gränssnitt för våra produkter:
public interface Button {}
public interface Select {}
public interface TextField {}
För varje operativsystem måste vi skapa gränssnittselement i samma stil som operativsystemet. Vi kommer att skriva kod för Windows och MacOS. Låt oss skapa implementeringar för Windows:
public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Nu gör vi samma sak för MacOS:
public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Excellent. Nu kan vi fortsätta till vår abstrakta fabrik, som kommer att skapa alla tillgängliga abstrakta produkttyper:
public interface GUIFactory {

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

}
Utmärkt. Som ni ser har vi inte gjort något komplicerat än. Allt som följer är också enkelt. I analogi med produkterna skapar vi olika fabriksimplementeringar för varje OS. Låt oss börja 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 till lite konsolutdata i metoderna och konstruktören för att ytterligare illustrera vad som händer. Nu för 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();
    }
}
Observera att varje metodsignatur indikerar att metoden returnerar en abstrakt typ. Men inuti metoderna skapar vi specifika implementeringar av produkterna. Detta är det enda stället vi kontrollerar skapandet av specifika instanser. Nu är det dags att skriva en klass för formuläret. Detta är en Java-klass vars fält är gränssnittselement:
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 fabrik som skapar gränssnittselement skickas till formulärets konstruktor. Vi kommer att skicka den nödvändiga fabriksimplementeringen till konstruktören för att skapa gränssnittselement för ett visst operativsystem.
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();
    }
}
Om vi ​​kör programmet på Windows får vi följande utdata:
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 blir utdata som följer:
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 :(
Och nu sammanfattar vi. Vi skrev skelettet till en GUI-baserad applikation där gränssnittselementen skapas specifikt för det relevanta operativsystemet. Vi ska kortfattat upprepa vad vi har skapat:
  • En produktfamilj som består av ett inmatningsfält, ett urvalsfält och en knapp.
  • Olika implementeringar av produktfamiljen för Windows och macOS.
  • En abstrakt fabrik som definierar ett gränssnitt för att skapa våra produkter.
  • Två implementeringar av vår fabrik, var och en ansvarig för att skapa en specifik produktfamilj.
  • Ett formulär (en Java-klass) vars fält är abstrakta gränssnittselement som initieras med de nödvändiga värdena i konstruktorn med hjälp av en abstrakt fabrik.
  • Applikationsklass Inuti denna klass skapar vi ett formulär som skickar den önskade fabriksimplementeringen till dess konstruktör.
Resultatet är att vi implementerade det abstrakta fabriksmönstret.

Abstrakt fabrik: hur man använder

En abstrakt fabrik är ett designmönster för att hantera skapandet av olika produktfamiljer utan att vara bunden till konkreta produktklasser. När du använder det här mönstret måste du:
  1. Definiera produktfamiljer. Anta att vi har två av dem:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. För varje produkt inom familjen, definiera en abstrakt klass (gränssnitt). I vårt fall har vi:
    • ProductA
    • ProductB
  3. Inom varje produktfamilj måste varje produkt implementera det gränssnitt som definierades i steg 2.
  4. Skapa en abstrakt fabrik, med metoder för att skapa varje produkt som definieras i steg 2. I vårt fall kommer dessa metoder att vara:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Skapa abstrakta fabriksimplementeringar så att varje implementering styr skapandet av produkter från en enda familj. För att göra detta, inuti varje implementering av den abstrakta fabriken, måste du implementera alla skapelsemetoder så att de skapar och returnerar specifika produktimplementationer.
Följande UML-diagram illustrerar instruktionerna ovan: Designmönster: Abstrakt fabrik - 3Nu kommer vi att skriva kod enligt dessa instruktioner:
// 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();
    }
}

Läxa

För att förstärka materialet kan du göra två saker:
  1. Förfina kaffebeställningsapplikationen så att den även fungerar på Linux.
  2. Skapa din egen abstrakta fabrik för att producera enheter som är involverade i alla militära strategier. Detta kan antingen vara en historisk militär strategi som involverar riktiga arméer, eller en fantasistrategi med orcher, tomtar och alver. Det viktiga är att välja något som intresserar dig. Var kreativ, skriv ut meddelanden på konsolen och njut av att lära dig om mönster!
Kommentarer
  • Populär
  • Ny
  • Gammal
Du måste vara inloggad för att lämna en kommentar
Den här sidan har inga kommentarer än