JavaRush /Java Blog /Random-TW /設計模式:工廠方法

設計模式:工廠方法

在 Random-TW 群組發布
你好!今天我們繼續學習設計模式,聊聊工廠方法。 設計模式:FactoryMethod - 1您將了解它是什麼以及該範本適合什麼任務。我們將在實踐中研究這種設計模式並探索其結構。為了讓您清楚地了解上述所有內容,您需要了解以下主題:
  1. Java 中的繼承。
  2. Java 中的抽象方法和類別。

工廠方法解決什麼問題?

在所有工廠設計模式中,都有兩組參與者-創建者(工廠本身)和產品(工廠創建的物件)。想像一下這種情況:我們有一家工廠生產 AutoRush 品牌的汽車。她知道如何創造具有不同類型車身的汽車模型:
  • 轎車
  • 旅行車
  • 轎跑車
我們的一切進展順利,有一天我們吸收了 OneAuto 的關注。作為明智的管理者,我們不想失去 OneAuto 客戶,我們的任務是重組生產,以便我們能夠生產:
  • AutoRush 轎車
  • AutoRush 旅行車
  • 轎跑車 AutoRush
  • OneAuto轎車
  • OneAuto 旅行車
  • 轎跑車OneAuto
正如您所看到的,出現的不是一組衍生產品,而是兩組,它們在一些細節上有所不同。工廠方法設計模式解決了創建不同組產品的問題,每個產品組都有一些特殊性。我們將在實踐中考慮這個模板的原理,以我們在之前的講座中創建的咖啡店為例,逐漸從簡單到複雜。

關於工廠模板的一些知識

讓我提醒您:我們與您一起建立了一家小型虛擬咖啡店。在其中,我們學習如何使用簡單的工廠製作不同類型的咖啡。今天我們將完善這個例子。讓我們記住我們的咖啡店和一個簡單的工廠是什麼樣子的。我們上了一堂咖啡課:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
還有他的幾位繼承人 - 我們工廠可以生產的特定類型的咖啡:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
為了方便接受訂單,我們推出了轉帳:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
咖啡工廠本身是這樣的:
public class SimpleCoffeeFactory {
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }

        return coffee;
    }
}
最後是咖啡店本身:
public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}

簡易工廠的現代化改造

我們的咖啡店生意很好。以至於我們正在考慮擴大規模。我們想開設幾個新點。作為有進取心的人,我們不會大量生產單調的咖啡店。我希望每一個都有自己的特色。因此,首先我們分兩點:意式和美式。這些變化不僅會影響內飾,還會影響飲料:
  • 在義大利咖啡店,我們會專門使用義大利品牌的咖啡,並經過特殊的研磨和烘焙。
  • 美國部分會稍大一些,每個訂單我們都會提供融化的棉花糖 - 棉花糖。
唯一不變的是我們的商業模式,它已經證明了自己。如果我們用程式語言說話,就會發生這樣的情況。我們有 4 類產品:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
就變成8:
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
由於我們希望保持目前的業務模型不變,因此我們希望方法orderCoffee(CoffeeType type)進行最少數量的更改。我們來看一下:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
我們有什麼選擇?我們已經知道如何編寫工廠了,對吧?立即想到的最簡單的事情就是編寫兩個類似的工廠,然後在建構函式中將所需的實作傳遞給我們的咖啡店。那麼咖啡店的等級就不會改變。首先,我們需要創建一個新的工廠類,繼承我們的簡單工廠並覆蓋createCoffee (CoffeeType type). 讓我們編寫用於製作義式和美式咖啡的工廠:
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
現在我們可以將所需的工廠實作傳遞給 CoffeeShop。讓我們看看從不同咖啡店訂購咖啡的代碼是什麼樣的。例如,義大利和美國風格的卡布奇諾:
public class Main {
    public static void main(String[] args) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
我們創建了兩個不同的咖啡店,並將每個咖啡店轉移到所需的工廠。一方面,我們實現了目標,但另一方面……有些東西正在抓撓企業家無法抑制的靈魂……讓我們找出問題所在。首先,工廠豐富。是否可以每次為一個新點創建自己的工廠,並確保在創建咖啡店時將所需的工廠傳遞給建構函數?其次,它仍然是一個簡單的工廠。只是現代化了一點。我們仍在研究一種新模式。第三,難道就不能採取不同的做法嗎?CoffeeShop如果我們能夠在課堂上在地化有關煮咖啡的所有問題,將製作咖啡和服務訂單的過程聯繫起來,但同時保持足夠的靈活性來製作不同風格的咖啡,那就太酷了。答案是肯定的,可以。這稱為工廠方法設計模式。

從簡單工廠到工廠方法

為了盡可能有效地解決問題,我們:
  1. 讓我們將方法傳回createCoffee(CoffeeType type)給類別CoffeeShop
  2. 讓我們把這個方法抽象化。
  3. 類別本身CoffeeShop將變得抽象。
  4. 班級CoffeeShop將有繼承人。
是的,朋友。義大利咖啡店只不過是該階層的繼承者,按照義大利咖啡師的最佳傳統CoffeeShop實施一種方法。createCoffee(CoffeeType type)所以,按順序。步驟 1. 讓我們將類別Coffee抽象化。我們現在有兩個不同產品系列。義大利和美國的咖啡飲料仍然有一個共同的祖先:Coffee. 將其抽象化是正確的:
public abstract class Coffee {
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
步驟 2. 使用CoffeeShop抽象方法使其抽象createCoffee(CoffeeType type)
public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
步驟 3. 創建義大利咖啡店,它是抽象咖啡店的後代類別。在其中,我們createCoffee(CoffeeType type)考慮到義大利的具體情況來實施該方法。
public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
步驟 4. 讓我們對美式咖啡店做同樣的事情。
public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
步驟 5. 讓我們來看看點一杯美式和義式拿鐵咖啡會是什麼樣子:
public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
恭喜。我們剛剛在我們的咖啡廳實現了工廠方法設計模式。

工廠方法如何運作

現在讓我們仔細看看我們得到了什麼。下圖顯示了產生的類別。綠色塊是創建者類,藍色塊是產品類。 設計模式:FactoryMethod - 2可以得到什麼結論?
  1. 所有產品都是抽象類別的實作Coffee
  2. 所有創建者都是抽象類別的實作CoffeeShop
  3. 我們觀察到兩個並行的類別層次結構:
    • 產品的層次結構。我們看到義大利後裔和美國後裔
    • 創作者的等級制度。我們看到義大利後裔和美國後裔
  4. 超類CoffeeShop沒有關於Coffee將創建哪個特定產品實現 ( ) 的資訊。
  5. 超類CoffeeShop將特定產品的創建委託給其後代。
  6. 每個後代類別根據其具體情況CoffeeShop實作一個工廠方法。createCoffee()換句話說,在創建者類別的實作中,根據創建者類別的具體情況決定準備特定的產品。
現在您已準備好定義工廠方法模式。 工廠方法模式定義了用於建立物件的接口,但允許子類別選擇要建立的實例的類別。因此,Factory方法將實例化操作委託給子類別。一般來說,記住定義並不像理解事物如何運作那麼重要。

工廠方法結構

設計模式:FactoryMethod - 3上圖展示了工廠方法模式的整體結構。這裡還有什麼重要的呢?
  1. Creator 類別包含與產品互動的所有方法的實作(工廠方法除外)。
  2. 抽象方法factoryMethod()必須由該類別的所有後代實作Creator
  3. 該類別實作了直接生產產品的ConcreteCreator方法。factoryMethod()
  4. 該類別負責創建特定的產品。這是唯一包含有關創建這些產品的資訊的課程。
  5. 所有產品都必須實作一個公共介面-成為公共產品類別的後代。這是必要的,以便使用產品的類別可以在抽象層級而不是具體實現層級對其進行操作。

家庭作業

所以,今天我們做了相當多的工作,研究了工廠方法設計模式。是時候鞏固您所學的材料了!任務 1. 努力開設另一家咖啡店。它可以用英語風格或西班牙語風格製作。或甚至是太空船的風格。讓我們在咖啡中添加食用色素,使其閃閃發亮,一般來說,咖啡將只是太空!任務 2. 在上一課中,您的任務是建立虛擬壽司吧或虛擬比薩店。你的任務不是停滯不前。今天您學習如何使用工廠方法模式來取得成功。是時候利用這些知識來擴展自己的業務了;)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION