《Head First 設計模式》:工廠方法模式

正文

一、定義

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

PS:在設計模式中,“實現一個接口”泛指實現某個超類型(可以是類或接口)的某個方法。

要點:

  • 通過子類來創建具體的對象。客戶只需要知道他們所使用的抽象類型即可。
  • 由子類決定要實例化的類是哪一個,是指在編寫創建者類時,不需要知道實際創建的產品是哪一個。選擇了使用哪個創建者子類,自然就決定了實際創建的產品是什麼。
  • 對象統一由定義好的工廠方法來創建。

二、實現步驟

1、創建產品抽象類

/**
 * 產品抽象類
 */
public abstract class Product {
    
    String name;
    
    public String getName() {
        return name;
    }
}

2、創建具體的產品,並繼承產品抽象類

(1)產品A1

/**
 * 產品A1
 */
public class ConcreteProductA1 extends Product {
    
    public ConcreteProductA1() {
        name = "ConcreteProductA1";
    }
}

(2)產品A2

/**
 * 產品A2
 */
public class ConcreteProductA2 extends Product {
    
    public ConcreteProductA2() {
        name = "ConcreteProductA2";
    }
}

(3)產品B1

/**
 * 產品B1
 */
public class ConcreteProductB1 extends Product {
    
    public ConcreteProductB1() {
        name = "ConcreteProductB1";
    }
}

(4)產品B2

/**
 * 產品B2
 */
public class ConcreteProductB2 extends Product {
    
    public ConcreteProductB2() {
        name = "ConcreteProductB2";
    }
}

3、創建創建者抽象類,並定義用來創建產品的工廠方法

創建者一般爲需要用到產品的類,需要的產品則通過類中的工廠方法創建。

/**
 * 創建者抽象類
 */
public abstract class Creator {

    /**
     * 創建產品(工廠方法)
     */
    protected abstract Product createProduct(String productType);
}

4、創建具體的創建者,並繼承創建者抽象類

具體的創建者需要實現創建產品的工廠方法。

(1)創建者1

/**
 * 創建者1
 */
public class ConcreteCreator1 extends Creator {

    @Override
    protected Product createProduct(String productType) {
        // 由具體的創建者(子類)決定創建哪個類的對象
        if ("A".equals(productType)) {
            return new ConcreteProductA1();
        } else if ("B".equals(productType)) {
            return new ConcreteProductB1();
        }
        return null;
    }
}

(2)創建者2

/**
 * 創建者2
 */
public class ConcreteCreator2 extends Creator {

    @Override
    protected Product createProduct(String productType) {
        // 由具體的創建者(子類)決定創建哪個類的對象
        if ("A".equals(productType)) {
            return new ConcreteProductA2();
        } else if ("B".equals(productType)) {
            return new ConcreteProductB2();
        }
        return null;
    }
}

5、創建者通過工廠方法創建產品

public class Test {
    
    public static void main(String[] args) {
        // 創建者1
        Creator creator1 = new ConcreteCreator1();
        // 創建者2
        Creator creator2 = new ConcreteCreator2();
        
        // 通過工廠方法創建產品
        Product product = creator1.createProduct("A");
        System.out.println("創建者1創建產品A:" + product.getName());
        product = creator2.createProduct("A");
        System.out.println("創建者2創建產品A:" + product.getName());
    }
}

三、舉個栗子

1、背景

假設你有一個披薩店,出售多種類型的披薩:芝士披薩、蛤蜊披薩、素食披薩等。由於經營有成,你打算推廣自己的加盟店。

爲了確保加盟店運營的質量,你希望加盟店能夠採用固定的製作流程。但是由於區域的差異,每家加盟店都可能想要提供不同風味的披薩(比如紐約、芝加哥、加州),因此又必須允許加盟店能夠自由地製作該區域的風味。

2、實現

披薩店子類通過實現創建披薩方法來決定要創建什麼風味的披薩。

(1)創建披薩抽象類

/**
 * 披薩抽象類
 */
public abstract class Pizza {
    
    /**
     * 名稱
     */
    String name;
    /**
     * 麪糰
     */
    String dough;
    /**
     * 醬料
     */
    String sauce;
    /**
     * 佐料
     */
    ArrayList<String> toppings = new ArrayList<>();
    
    void prepare() {
        System.out.println("Preparing " + name);
        System.out.println("Tossing dough...");
        System.out.println("Adding souce...");
        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;
    }
}

(2)創建不同風味、不同類型的披薩

/**
 * 紐約風味的芝士披薩
 */
public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() {
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        toppings.add("Grated Reggiano Cheese");
    }
}
/**
 * 紐約風味的蛤蜊披薩
 */
public class NYStyleClamPizza extends Pizza {

    public NYStyleClamPizza() {
        name = "NY Style Sauce Clam Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        toppings.add("Fresh Clams");
    }
}
/**
 * 芝加哥風味的芝士披薩
 */
public class ChicagoStyleCheesePizza extends Pizza {

    public ChicagoStyleCheesePizza() {
        name = "Chicago Style Deep Dish Cheese Pizza";
        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");
    }
}
/**
 * 芝加哥風味的蛤蜊披薩
 */
public class ChicagoStyleClamPizza extends Pizza {

    public ChicagoStyleClamPizza() {
        name = "Chicago Style Clam Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";
        toppings.add("Frozen Clams");
    }
    
    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

(3)創建披薩店抽象類

/**
 * 披薩店抽象類
 */
public abstract class PizzaStore {
    
    /**
     * 訂購披薩
     */
    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    /**
     * 創建披薩(工廠方法)
     */
    protected abstract Pizza createPizza(String type);
}

(4)創建不同風味的披薩店

/**
 * 紐約風味披薩店
 */
public class NYStylePizzaStore extends PizzaStore {

    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            pizza = new NYStyleCheesePizza(); 
        } else if ("clam".equals(type)) {
            pizza = new NYStyleClamPizza();
        }
        return pizza;
    }
}
/**
 * 芝加哥風味披薩店
 */
public class ChicagoStylePizzaStore extends PizzaStore {

    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            pizza = new ChicagoStyleCheesePizza(); 
        } else if ("clam".equals(type)) {
            pizza = new ChicagoStyleClamPizza();
        }
        return pizza;
    }
}

(5)使用不同風味的披薩店訂購披薩

public class Test {
    
    public static void main(String[] args) {
        // 紐約風味披薩店
        PizzaStore nyStore = new NYStylePizzaStore();
        // 芝加哥風味披薩店
        PizzaStore chicagoStore = new ChicagoStylePizzaStore();
        
        // 訂購芝士披薩
        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");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章