工廠模式讀書筆記

簡單工廠模式

在我們平時的編碼過程中,我們會經常使用到new,而當我們使用new的時候,就會想到"具體",因爲我們的確是在實例化一個具體類,這樣子,可能會導致代碼更脆弱。比如說,我們可能會寫出以下代碼:

Duck duck;
if(picnic){
duck = new MallardDuck();
}else if (hunting){
duck = new DecoyDuck();
}else if(inBathTub){
duck = new RubberDuck();
}

當出現這種代碼時,一旦需求有變化,就必須重新打開這段代碼進行檢查和修改。通常這樣修改過的diam將造成部分系統更難維護和更新,而且更容易犯錯。

披薩的例子

當我們訂購一個披薩的時候,我們可能會寫出如下代碼:

public class PizzaStore{
    Pizza orderPizza(String type){
    	Pizza pizza;
    	//根據披薩的類型,我們實例化正確的具體類
    	if(type.equals("cheese")){
    		pizza = new CheesePizza();
    	}else if(type.equals("greek")){
    		pizza = new GreekPizza();
    	}else if(type.equals("pepperoni")){
    		pizza = new PepperoniPizza();
    	}
	    pizza.prepare();
	    pizza.bake();
	    pizza.cut();
	    pizza.box();
	    return pizza;
    }
}

但是一旦我們pizza的種類變化,我們就不得不修改if else語句。因此我們需要把這一部分進行封裝。

建立簡單工廠

public class SimplePizzaFactory{
	public Pizza createPizza(String type){
		Pizza pizza = null;
		if(type.equals("cheese")){
    		pizza = new CheesePizza();
    	}else if(type.equals("greek")){
    		pizza = new GreekPizza();
    	}else if(type.equals("pepperoni")){
    		pizza = new PepperoniPizza();
    	}
		return pizza;
	}
}

除此之外,我們還可以將工廠定義爲靜態方法,這是我們常見的一個技巧,因爲不需要使用創建對象的方法來實例化對象,但是這也有確定,不能通過集成來改變創建方法的行爲。

我們的PizzaStore變成了如下:

 public class PizzaStore{
    SimplePizzaFactory factory;
	public PizzaStore(SimplePizzaFactory factory){
		this.factory = factory;
	}
      Pizza orderPizza(String type){
        	Pizza pizza;
        	pizza = factory.createPizza(type);
    	    pizza.prepare();
    	    pizza.bake();
    	    pizza.cut();
    	    pizza.box();
    	    return pizza;
        }
    }

定義簡單工廠

簡單工廠其實不是一個設計模式,它比較像是一種編程習慣,但是我們會經常用到它。

工廠模式

假設我們又增加了新的需求,我們的pizza店需要生產更多的披薩,我們需要根據不同區域的口味,生成不同種類的披薩,這時我們的PizzaStore就會變成這樣
在這裏插入圖片描述
這樣就會有越來越多的工廠,分別產生不同口味的披薩。因此,我們可以通過創建多個“披薩店”分別製造不同口味的披薩,除此之外,我們加工披薩的流程也會產生差異,因此我們需要對我們的pizzaStore做出些改變,將加工pizza的流程和pizza的製作流程捆綁在一起的同時又具有一定彈性。

代碼

我們的PizzaStore現在變成了這樣:

PizzaStore.java

public abstract class PizzaStore {

    // 原本是由一個類類負責所有具體類的實例化,現在變成由一堆子類來負責實例化

    public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    protected abstract Pizza createPizza(String type);//工廠方法是抽象的,所以依賴子類來處理對象的創建。 工廠方法必須返回一個對象
}

NYPizzaStore.java

public class NYPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("cheese")){
            return new NYStyleCheesePizza();
        }else if (type.equals("veggie")){
            return new NYStyleVeggiePizza();
        }else {
            return null;
        }
    }
}

ChicagoPizzaStore.java

public class ChicagoPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("cheese")){
            return new ChicagoStyleCheesePizza();
        }else {
            return null;
        }
    }
}

我們讓PizzaStore的各個子類負責定義自己的createPizza()方法,所以我們會得到一些PizzaStore具體的子類。

在這裏插入圖片描述

那子類是如何做決定的呢?當orderPizza()調用createPizza()方法的時候,某個披薩店的子類(NYStylePizzaStore或者ChicagoStylePizzaStore)來負責創建披薩。

下面,我們看一下我們的Pizza類:
Pizza.java

import java.util.ArrayList;

public abstract class Pizza {

    String name;
    String dough;
    String sauce;

    ArrayList toppings = new ArrayList();

    void prepare(){
        System.out.println("Preparing " + name );
        System.out.println("Tossing dough...");
        System.out.println("Ading sauce...");
        System.out.println("Adding toppings: ");
        for (int i = 0; i < toppings.size(); i++){
            System.out.println(" " + toppings.get(i));
        }
    }

    void bake(){
        System.out.println("Bake for 25 minutes at 350");
    }

    void cut(){
        System.out.println("Cutting the pizza into diagonal slices");
    }

    void box(){
        System.out.println("Place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }
}

現在我們需要一些具體子類,來定義紐約和芝加哥風味的芝士披薩。
芝加哥風味的:
ChicagoStyleCheesePizza.java

public class ChicagoStyleCheesePizza extends Pizza {
    public ChicagoStyleCheesePizza(){
        name = "Chicago Style Deep Dish Cheese Pizzza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";
        toppings.add("Shredded Mozzarella Cheese");
    }

    void cut(){
        System.out.println("Cutting the pizza into square slices");
    }
}

紐約風味的
NYStyleCheesePizza.java

public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza(){
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Cruse Dough";
        sauce = "Marinara Sauce";
        toppings.add("Grated Reggiano Cheese");
    }
}

NYStyleVeggiePizza.java

public class NYStyleVeggiePizza extends Pizza {

    public NYStyleVeggiePizza(){
        name = "NY Style Sauce and Veggie Pizza";
        dough = "Thin Cruse Dough";
        sauce = "Marinara Sauce";
        toppings.add("Grated Reggiano Veggie");
    }
}

測試類:

public class PizzaTestDrive {
    public static void main(String[] args){
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a "+ pizza.getName() + "\n");

        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel Ordered a "+ pizza.getName() + "\n");
    }
}

工廠模式類圖

創建者類:
在這裏插入圖片描述
所有工廠模式都是用來封裝對象的創建。工廠模式通過讓子類決定該創建對象是什麼,來達到將對象創建的過程封裝的目的。
PizzaStore裏面定義了一個抽象的工廠方法createPizza(),讓子類實現此方法,來製造產品。這些子類稱爲具體創建者。

產品類:

在這裏插入圖片描述
上圖這些就是具體的產品。

定義工廠模式

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

工廠模式類圖

在這裏插入圖片描述

所有的產品必須實現這個共同的接口Product,這樣,使用這些產品的類就可以引用這個接口,而不是具體類。
Creator是一個類,它實現了所有操縱產品的方法,但是它不實現工廠方法。Creator的子類必須實現factoryMethod這個抽象方法,來實際製造出產品。ConcreteCreator類負責創建一個或多個具體的ConcreteProduct。

工廠模式和簡單工廠比較

簡單工廠的做法,可以將對象的創建封裝起來,在一個地方都處理完了;但是工廠方法只是提供了一個框架,由子類決定要如何實現。

依賴倒置原則

不能讓高層組件依賴底層組件,無論是高層組件還是底層組件,都應該依賴於抽象。

抽象工廠

我們的披薩製造過程中是由不同的原料組成的,紐約風味的披薩使用一組原料,而芝加哥使用另一組原料。每一種原料都包含一種棉短,一種醬料,一種芝士以及一種海鮮。因此,我們需要處理一下這些原料。

建造原料工廠

public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClam();
}

在這個接口中,每個原料都有一個對應的方法創建該原料。我們要做的是,爲每一種區域都建造一個工廠,比如說,我們先創建紐約原料工廠。

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

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

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

    @Override
    public Veggies[] createVeggies() {
        Viggies viggies[] = {new Garlic(), new Onion(),new MushRoom()};
        return viggies;
    }

    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    @Override
    public Clams createClam() {
        return new FreshClams();
    }
}

重做pizza

import java.util.ArrayList;

public abstract class Pizza {

    String name;
    Dough dough;
    Sause sauce;

    ArrayList toppings = new ArrayList();

   abstract void prepare();

    void bake(){
        System.out.println("Bake for 25 minutes at 350");
    }

    void cut(){
        System.out.println("Cutting the pizza into diagonal slices");
    }

    void box(){
        System.out.println("Place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }
}

重做CheesePizza

public class CheesePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;
    public CheesePizza(PizzaIngredientFactory ingredientFactory){
        this.ingredientFactory = ingredientFactory;
    }
    @Override
    void prepare() {
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
    }
}

再看我們的披薩店

public class NYPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        // 紐約店會用到紐約披薩原料工廠,由該工廠負責生產所有紐約風味比薩所需的原料。
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
        if (type.equals("cheese")){
            pizza = new CheesePizza(ingredientFactory);
            return pizza;
        }else if (type.equals("veggie")){
            // TODO 
            
        }
    }
}

我們公共抽象工廠所提供的接口,可以創建產品家族,利用這個接口,我們的代碼將從實際的工廠解耦。

定義抽象工廠模式

抽象工廠模式提供了一個接口,用於創建相關或依賴對象的家族,而不需要明確指明具體類。
在這裏插入圖片描述

比較工廠方法和抽象工廠

工廠方法和抽象工廠都是負責創建對象,但是工廠方法使用的是繼承,但是抽象工廠使用的是對象的組合。這就意味着,利用工廠方法創建對象,需要擴展一個類,並覆蓋他的工廠方法。但是抽象工廠提供了一個用來創建一個產品家族的抽象類型,這個類型的子類定義了產品被產生的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章