二、工廠模式

定義

工廠模式(Factory Method)是由一個工廠對象決定創建出哪一種產品類的實例。工廠類來封裝實例化對象的行爲,將產品的 “實現” 和 “使用” 解耦。

工廠模式有三類:簡單工廠模式工廠方法模式抽象工廠模式

工廠模式屬於創建型模式。

 

要點

抽象工廠(Abstract Factory):提供創建產品的接口,調用者通過它訪問具體工廠的實現方法來創建產品。
具體工廠(ConcreteFactory):實現抽象工廠中的抽象方法來完成具體產品的創建。
抽象產品(Product):定義產品規範,描述產品主要特性和功能。
具體產品(ConcreteProduct):抽象產品的特定需求來實現,由具體工廠來創建,它同具體工廠之間一一對應。

 

場景

當使用 new 實例化一個具體類時,代碼綁着具體類會讓代碼更脆弱,缺乏彈性。在軟件開發中,當我們會用到大量的創建某種、某類或者某批對象時,就會使用到工廠模式。

例如有披薩店,客戶從 店鋪點餐檯(OrderPizza) 下單想要的披薩:

Pizza orderPizza(String pizzaType) {
	Pizza pizza;
	if(pizzaType.equals("Pepper")) {
		pizza = new PepperPizza();
	} else if (pizzaType.equals("Greek")) {
		pizza = new GreekPizza();
	} else if (pizzaType.equals("Cheese")) {
		pizza = new CheesePizza();
	}
	pizza.prepare();
	pizza.bake();
	pizza.cut();
	pizza.box();
	return pizza;
}

但是需求發生變化時,例如店鋪上架、下架一些 Pizza ,就必須重新打開這段代碼進行檢查和修改,已經違反了開閉原則,而且點餐檯不應該考慮披薩的製作,應該交給 後廚(披薩工廠,Factory) 負責處理。

 

實現

這裏主要用代碼來演示,可以在Github的鏈接中獲取源碼。

1. 簡單工廠模式

簡單工廠模式是由 一個工廠對象決定創建出哪一種產品類的實例 。簡單工廠模式不是一個設計模式,而是一種編程習慣。

if(pizzaType.equals("Pepper")) {
	pizza = new PepperPizza();
} else if (pizzaType.equals("Greek")) {
	pizza = new GreekPizza();
} else if (pizzaType.equals("Cheese")) {
	pizza = new CheesePizza();
}

我們需要把上面的部分代碼抽離出來搬到另一個對象中,這個新對象只需要負責創建 Pizza,這個新對象就是 “工廠”,OrderPizza 只需要關心從工廠得到一個披薩就行:

public class SimplePizzaFactory {

	public Pizza createPizza(String pizzaType) {
		Pizza pizza;
		switch (pizzaType) {
            case "Pepper":
                pizza = new PepperPizza();
                pizza.setPizzaName("胡椒風味披薩");
                return pizza;
            case "Greek":
                pizza = new GreekPizza();
                pizza.setPizzaName("希臘風味披薩");
                return pizza;
            case "Cheese":
                pizza = new CheesePizza();
                pizza.setPizzaName("芝士風味披薩");
                return pizza;
            default:
                return null;
        }
	}
	
}

 

示例

需求:披薩項目,需要方便披薩種類的擴展,便於維護,定義一個可以實例化 Pizaa 對象的類,封裝創建對象的代碼。
1)披薩的種類很多(比如 GreekPizz、CheesePizz 等)
2)披薩的製作有 prepare,bake, cut, box
3)完成披薩店訂購功能。

披薩類(抽象)

/**
 * Pizza 抽象類
 */
public abstract class Pizza {

    /**
     * 披薩類型
     */
    protected String pizzaType;

    /**
     * 準備原材料, 不同的披薩不一樣,做成抽象方法
     */
    public abstract void prepare();

    /**
     * 烘烤
     */
    public void bake() {
        System.out.println(pizzaType + "baking...");
    }

    /**
     * 切塊
     */
    public void cut() {
        System.out.println(pizzaType + "cutting...");
    }

    /**
     * 包裝
     */
    public void box() {
        System.out.println(pizzaType + "boxing...");
    }

    public void setPizzaType(String pizzaType) {
        this.pizzaType = pizzaType;
    }

}

胡椒風味披薩

/**
 * 胡椒風味披薩
 */
public class PepperPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("給胡椒風味披薩準備原材料");
    }

}

希臘風味披薩

/**
 * 希臘風味披薩
 */
public class GreekPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("給希臘風味披薩準備原材料");
    }

}

芝士風味披薩

/**
 * 芝士風味披薩
 */
public class CheesePizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("給芝士風味披薩準備原材料");
    }

}

簡單工廠類

/**
 * 簡單披薩工廠類
 */
public class SimplePizzaFactory {

    public Pizza createPizza(String pizzaType) {
        Pizza pizza;
        switch (pizzaType) {
            case "PepperPizza":
                pizza = new PepperPizza();
                pizza.setPizzaType("胡椒風味披薩");
                return pizza;
            case "GreekPizza":
                pizza = new GreekPizza();
                pizza.setPizzaType("希臘風味披薩");
                return pizza;
            case "CheesePizza":
                pizza = new CheesePizza();
                pizza.setPizzaType("芝士風味披薩");
                return pizza;
            default:
                return null;
        }
    }

}

披薩商店

/**
 * 披薩商店
 */
public class PizzaStore {

    private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));

    /**
     * 給披薩商店供貨的披薩工廠( 後廚 )
     */
    private SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    /**
     * 披薩製作流程
     */
    public void orderPizza() {
        String pizzaType;
        Pizza pizza;
        do {
            pizzaType = getType();
            // 使用簡單工廠創建實例,解耦!解耦!解耦!
            pizza = this.factory.createPizza(pizzaType);

            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("披薩暫時缺貨!請重新訂購。");
            }
        } while (true);
    }

    /**
     * 獲取客戶訂購的披薩種類
     *
     * @return 披薩類型
     */
    private static String getType() {
        System.out.println("\n請輸入披薩類型:");
        try {
            return READER.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

}

這裏的工廠,將對象實例化抽離出來,解耦。

源代碼

Click here

 

2. 工廠方法模式

工廠方法模式定義了一個創建產品對象的接口,需要子類決定需要實例化的類是哪一個。工廠方法模式將對象的實例化推遲到子類。

在這裏插入圖片描述

示例

新需求:
有了加盟披薩店,根據加盟店地域的飲食口味不同,每家加盟店可以提供地域特色的披薩,比如 北京風味的胡椒披薩、倫敦風味的胡椒披薩。

分析:
如果使用簡單工廠模式創建不同的簡單工廠類,比如 BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等,那麼每家工廠都有了各自的製作流程,這是不行的,必須符合總店的製作流程規範才行,然後,這些加盟店可以自由地製作符合該區域特色風味的披薩。

將披薩的製作流程做成抽象方法,在不同的披薩工廠中具體實現。

北京胡椒風味披薩

/**
 * 北京胡椒風味披薩
 */
public class BJPepperPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("給北京胡椒風味披薩準備原材料");
    }

}

倫敦胡椒風味披薩

/**
 * 倫敦胡椒風味披薩
 */
public class LDPepperPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("給倫敦胡椒風味披薩準備原材料");
    }

}

披薩商店(抽象)

/**
 * 披薩商店(抽象)
 */
public abstract class AbstractPizzaStore {

    private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));

    /**
     * 定義抽象方法createPizza, 讓各個工廠子類自己實現當地的特色風味披薩
     */
    protected abstract Pizza createPizza(String pizzaType);

    /**
     * 披薩製作流程,所有的加盟店都需要遵守
     */
    protected final void orderPizza() {
        String pizzaType;
        Pizza pizza;
        do {
            pizzaType = getType();
            // 顧客決定工廠,子類工廠決定披薩,解耦!解耦!解耦!
            pizza = createPizza(pizzaType);

            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("披薩暫時缺貨!請重新訂購。");
            }
        } while (true);
    }

    /**
     * 獲取客戶訂購的披薩種類
     *
     * @return 披薩類型
     */
    private String getType() {
        System.out.println("\n請輸入披薩類型:");
        try {
            return READER.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}

北京披薩商店

/**
 * 北京披薩商店
 */
public class BJPizzaStore extends AbstractPizzaStore {

    @Override
    public Pizza createPizza(String pizzaType) {
        Pizza pizza;
        switch (pizzaType) {
            case "PepperPizza":
                pizza = new BJPepperPizza();
                pizza.setPizzaType("北京胡椒風味披薩");
                return pizza;
            case "GreekPizza":
                pizza = new BJGreekPizza();
                pizza.setPizzaType("北京希臘風味披薩");
                return pizza;
            case "CheesePizza":
                pizza = new BJCheesePizza();
                pizza.setPizzaType("北京芝士風味披薩");
                return pizza;
            default:
                return null;
        }
    }

}

倫敦披薩商店

/**
 * 倫敦披薩商店
 */
public class LDPizzaStore extends AbstractPizzaStore {

    @Override
    public Pizza createPizza(String pizzaType) {
        Pizza pizza;
        switch (pizzaType) {
            case "PepperPizza":
                pizza = new LDPepperPizza();
                pizza.setPizzaType("倫敦胡椒風味披薩");
                return pizza;
            case "GreekPizza":
                pizza = new LDGreekPizza();
                pizza.setPizzaType("倫敦希臘風味披薩");
                return pizza;
            case "CheesePizza":
                pizza = new LDCheesePizza();
                pizza.setPizzaType("倫敦芝士風味披薩");
                return pizza;
            default:
                return null;
        }
    }

}

這裏的工廠生產一種產品,那就是披薩。

源代碼

Click here

 

3. 抽象工廠模式

抽象工廠模式提供一個接口,用於創建相關或依賴對象的家族,而無需指明具體的類。

抽象工廠允許客戶使用抽象的接口來創建一組相關的產品,而不需要知道(或關心)實際產出的具體產品是什麼,客戶就從具體的產品中被解耦。

在這裏插入圖片描述

示例

需求:
工廠模式讓所有加盟店使用總店的製作流程規範,爲了防止加盟店使用廉價的食材增加利潤,這可能會損害品牌形象,我們需要建立一家原料工廠來防止這樣的事情發生。但是,不同地域的食材可能是不一樣的。

這次的工廠面向的是披薩原料。

醬料

/**
 * 醬料類型
 */
public interface Sauce {

    /**
     * 獲取醬料類型名
     */
    String getSauceType();

}
/**
 * 麪糰類型
 */
public interface Dough {

    /**
     * 獲取麪糰類型名
     */
    String getDoughType();

}
/**
 * 芝士類型
 */
public interface Cheese {

    /**
     * 獲取芝士類型名
     */
    String getCheeseType();

}

披薩(抽象)

/**
 * 披薩(抽象)
 */
public abstract class AbstractPizza {

    /**
     * 披薩類型
     */
    protected String pizzaType;
    /**
     * 披薩的醬料
     */
    protected Sauce sauce;
    /**
     * 披薩的麪糰
     */
    protected Dough dough;
    /**
     * 披薩的芝士
     */
    protected Cheese cheese;

    /**
     * 基本的做法(具體種類的披薩去實現)
     */
    public abstract void prepare();

    /**
     * 烘烤
     */
    public void bake() {
        System.out.println( pizzaType + "baking...");
    }

    /**
     * 切塊
     */
    public void cut() {
        System.out.println(pizzaType + "cutting...");
    }

    /**
     * 包裝
     */
    public void box() {
        System.out.println(pizzaType + "boxing...");
    }

    public void setPizzaType(String pizzaType) {
        this.pizzaType = pizzaType;
    }

}

北京風味的芝士披薩

/**
 * 北京風味的芝士披薩
 */
public class BJCheesePizza extends AbstractPizza {

    private PizzaIngredientFactory ingredientFactory;

    public BJCheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    public void prepare() {
        System.out.println(pizzaType + " preparing...");
        sauce = ingredientFactory.createSauce();
        System.out.println("sauce: " + sauce.getSauceType());
        dough = ingredientFactory.createDough();
        System.out.println("dough: " + dough.getDoughType());
        cheese = ingredientFactory.createCheese();
        System.out.println("cheese: " + cheese.getCheeseType() + "\n");
    }

}

倫敦風味的芝士披薩

/**
 * 倫敦風味的芝士披薩
 */
public class LDCheesePizza extends AbstractPizza {

    private PizzaIngredientFactory ingredientFactory;

    public LDCheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    public void prepare() {
        System.out.println(pizzaType + " preparing...");
        sauce = ingredientFactory.createSauce();
        System.out.println("sauce: " + sauce.getSauceType());
        dough = ingredientFactory.createDough();
        System.out.println("dough: " + dough.getDoughType());
        cheese = ingredientFactory.createCheese();
        System.out.println("cheese: " + cheese.getCheeseType() + "\n");
    }
}

披薩商店(抽象)

/**
 * 披薩商店(抽象)
 */
public abstract class AbstractPizzaStore {

    private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));

    /**
     * 讓子類自己實現
     */
    protected abstract AbstractPizza createPizza(String pizzaType);

    /**
     * 披薩製作流程
     */
    protected final void orderPizza() {
        String pizzaType;
        AbstractPizza pizza;
        do {
            pizzaType = getType();
            pizza = createPizza(pizzaType);

            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("披薩暫時缺貨!請重新訂購。");
            }
        } while (true);
    }

    /**
     * 獲取客戶訂購的披薩種類
     *
     * @return 披薩類型
     */
    private String getType() {
        System.out.println("\n請輸入披薩類型:");
        try {
            return READER.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}

倫敦披薩商店

/**
 * 倫敦披薩商店
 */
public class BJPizzaStore extends AbstractPizzaStore {

    private PizzaIngredientFactory ingredientFactory = new BJPizzaIngredientFactory();

    @Override
    protected AbstractPizza createPizza(String pizzaType) {
        AbstractPizza pizza;

        switch (pizzaType) {
            case "CheesePizza":
                pizza = new BJCheesePizza(ingredientFactory);
                pizza.setPizzaType("北京芝士風味披薩");
                return pizza;
            default:
                return null;
        }
    }

}

倫敦披薩商店

/**
 * 倫敦披薩商店
 */
public class LDPizzaStore extends AbstractPizzaStore {

    private PizzaIngredientFactory ingredientFactory = new LDPizzaIngredientFactory();

    @Override
    protected AbstractPizza createPizza(String pizzaType) {
        AbstractPizza pizza;

        switch (pizzaType) {
            case "CheesePizza":
                pizza = new LDCheesePizza(ingredientFactory);
                pizza.setPizzaType("倫敦風味披薩");
                return pizza;
            default:
                return null;
        }
    }

}

食材工廠定義接口(抽象工廠)

/**
 * 爲食材工廠定義接口
 */
public interface PizzaIngredientFactory {

    /**
     * 麪糰
     */
    Dough createDough();

    /**
     * 醬料
     */
    Sauce createSauce();

    /**
     * 芝士
     */
    Cheese createCheese();

}

北京披薩食材工廠

/**
 * 北京披薩食材工廠
 */
public class BJPizzaIngredientFactory implements PizzaIngredientFactory {

    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }

    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

}

倫敦披薩食材工廠

/**
 * 倫敦披薩食材工廠
 */
public class LDPizzaIngredientFactory implements PizzaIngredientFactory {

    @Override
    public Sauce createSauce() {
        return new PlumTomatoSauce();
    }

    @Override
    public Dough createDough() {
        return new ThickCrustDough();
    }

    @Override
    public Cheese createCheese() {
        return new MozzarellaCheese();
    }

}

這裏的食材工廠可以生產多種食材,而不是一種食材。

源代碼

Click here

 

總結

使用工廠模式的好處在於面向接口編程,它可以將產品的 “使用” 和 “生產” 解耦,將創建對象的代碼集中在一個地方,可以避免代碼中的重複,方便後期維護。工廠模式很好地結合了 依賴倒置原則(要依賴抽象,不要依賴具體) ,想要遵循依賴倒置原則,工廠方法是很好的技巧。

  • 所有的工廠都是用來封裝對象的創建。
  • 所有工廠模式都通過減少應用程序和具體類之間的依賴實現鬆耦合。
  • 工廠方法模式使用繼承形式,對象的創建延遲到子類進行
  • 工廠方法模式創建一類產品,子類實現工廠方法來創建對象。
  • 抽象工廠模式使用對象組合形式,對象的創建被實現在工廠接口所暴露出來的方法中
  • 抽象工廠模式創建相關的產品家族,而不需要依賴它們的具體類。

舉例:在JDK的 Calender 日期類中就使用工廠模式來創建不同的日期實例類。

發佈了21 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章