工廠模式案例詳解

  • 2020 年 7 月 21 日
  • 筆記

簡單工廠模式

看一個具體的需求

看一個披薩的項目:要便於披薩種類的擴展,要便於維護。

  • 披薩的種類很多(比如GreekPizza、CheesePizza等);
  • 披薩的製作有prepare,bake,cut,box;
  • 完成披薩店訂購功能。

使用傳統的方式完成

在這裡插入圖片描述

//把Pizza類做成抽象類
public abstract class Pizza {
    private String name;//名字

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

    public void bake() {
        System.out.println(name + " baking");
    }

    public void cut() {
        System.out.println(name + " cutting");
    }

    public void box() {
        System.out.println(name + " boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class GreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("給GreekPizza準備原材料");
    }
}
public class CheessPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("給CheessPizza準備原材料");
    }
}
public class OrderPizza {
    //構造方法
    public OrderPizza() {
        Pizza pizza = null;
        String orderType;//訂購披薩的類型
        do {
            orderType = getType();
            if (orderType.equals("greek")) {
                pizza = new GreekPizza();
                pizza.setName("GreekPizza");
            } else if (orderType.equals("cheess")) {
                pizza = new CheessPizza();
                pizza.setName("CheessPizza");
            } else {
                break;
            }
            //輸出pizza製作過程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    //寫一個方法,可以獲取客戶希望訂購的披薩種類
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
//相當於客戶端
public class OrderStore {
    public static void main(String[] args) {
        OrderPizza orderPizza = new OrderPizza();
    }
}

傳統方式的優缺點

  • 優點是比較好理解,簡單易操作;
  • 缺點是違反了設計模式的ocp原則,即對擴展開放,對修改關閉。即當我們給類增加新功能的時候,盡量不修改程式碼,或者盡量少修改程式碼;
  • 比如這時我們要新增加一個Pizza的種類(Pepper披薩),我們需要做的修改較大。

改進的思路分析

  • 分析:修改程式碼可以接受,但是如果我們在其它的地方也有創建Pizza的程式碼,就意味著,也需要修改,而創建Pizza的程式碼,往往有多處;
  • 思路:把創建Pizza對象封裝到一個類中,這樣我們有新的Pizza種類時,只需要修改該類就可,其它有創建到Pizza對象的程式碼就不需要修改了(使用簡單工廠模式)。

簡單工廠模式

簡單工廠模式是屬於創建型模式,是工廠模式的一種。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式。

簡單工廠模式:定義了一個創建對象的類,由這個類來封裝實例化對象的行為。
在軟體開發中,當我們會用到大量的創建某種、某類或者謀批對象時,就會使用到工廠模式。

簡單工廠模式的設計方案:定義一個可以實例化Pizza對象的類,封裝創建對象的程式碼。
在這裡插入圖片描述

//簡單工廠類
public class SimpleFactory {
    //簡單工廠模式,也叫靜態工廠模式,可以用static修飾該方法
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("greek")) {
            pizza = new GreekPizza();
            pizza.setName("GreekPizza");
        } else if (orderType.equals("cheess")) {
            pizza = new CheessPizza();
            pizza.setName("CheessPizza");
        }
        //輸出pizza製作過程
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
public class OrderPizza2 {
    //定義一個簡單工廠對象
    SimpleFactory simpleFactory;

    //構造器
    public OrderPizza2(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    Pizza pizza = null;

    public void setFactory(SimpleFactory simpleFactory) {
        String orderType = "";//用戶輸入的
        this.simpleFactory = simpleFactory;//設置簡單工廠對象
        do {
            orderType = getType();
            pizza = this.simpleFactory.createPizza(orderType);
            //輸出pizza製作過程
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("訂購失敗");
                break;
            }
        } while (true);
    }

    //寫一個方法,可以獲取客戶希望訂購的披薩種類
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
//相當於客戶端
public class OrderStore {    
  public static void main(String[] args) {        
    OrderPizza2 orderPizza = new OrderPizza2(new SimpleFactory());    
  }
}

工廠方法模式

披薩項目新的需求:客戶在點披薩時,可以點不同口味的披薩,比如「北京的乳酪披薩」、「北京的胡椒披薩」或者是「倫敦的乳酪披薩」、「倫敦的胡椒披薩」。

思路一:使用簡單工廠模式,創建不同的簡單工廠類,比如BJPizzaSimpleFactory、LDPizzzaSimpleFactory等等,從當前這個案例來說,也是可以的,但是考慮到項目的規模,以及軟體的可維護性 、可擴展性並不是特別好。

思路二:使用工廠方法模式。將披薩項目的實例化功能抽象成抽象方法,在不同口味點餐子類中具體實現。

工廠方法模式定義了一個創建對象的抽象方法,由子類決定要實例化的類。工廠方法模式將對象的實例化推遲到子類。
在這裡插入圖片描述

//把Pizza類做成抽象類
public abstract class Pizza {
    private String name;//名字

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

    public void bake() {
        System.out.println(name + " baking");
    }

    public void cut() {
        System.out.println(name + " cutting");
    }

    public void box() {
        System.out.println(name + " boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class BJCheessPizza extends Pizza {
    @Override
    public void prepare() {
        setName("北京的CheessPizza");
        System.out.println("  北京的CheessPizza 準備原材料");
    }
}
public class BJPepperPizza extends Pizza {
    @Override
    public void prepare() {
        setName("北京的Pepper");
        System.out.println("  北京的Pepper 準備原材料");
    }
}
public class LDCheessPizza extends Pizza {
    @Override
    public void prepare() {
        setName("倫敦的CheessPizza");
        System.out.println("  倫敦的CheessPizza 準備原材料");
    }
}
public class LDPepperPizza extends Pizza {
    @Override
    public void prepare() {
        setName("倫敦的Pepper");
        System.out.println("  倫敦的Pepper 準備原材料");
    }
}
public abstract class OrderPizza {
    //構造方法
    public OrderPizza() {
        Pizza pizza = null;
        String orderType;//訂購披薩的類型
        do {
            orderType = getType();
            //調用方法
            pizza = creatPizza(orderType);
            //輸出pizza製作過程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    //定義一個抽象方法,讓各個工廠子類自己實現
    abstract Pizza creatPizza(String orderType);

    //寫一個方法,可以獲取客戶希望訂購的披薩種類
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
public class BJOrderPizza  extends OrderPizza {
    @Override
    Pizza creatPizza(String orderType) {
        Pizza pizza=null;
        if (orderType.equals("Cheess")){
            pizza=new BJCheessPizza();
        }else if(orderType.equals("Pepper")){
            pizza=new BJPepperPizza();
        }
        return pizza;
    }
}
public class LDOrderPizza extends OrderPizza {
    @Override
    Pizza creatPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("Cheess")) {
            pizza = new LDCheessPizza();
        } else if (orderType.equals("Pepper")) {
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}
//披薩店
public class PizzaStore {
    public static void main(String[] args) {
        new BJOrderPizza();//創建北京各種口味的披薩
        new LDOrderPizza();//創建倫敦各種口味的披薩
    }
}

抽象工廠模式

抽象工廠模式:定義了一個interface用於創建相關或有依賴關係的對象簇,而無需指明具體的類。

抽線工廠模式可以將簡單工廠模式和工廠方法模式進行整合。

從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者 稱為進一步的抽象)。

將工廠抽象成兩層,AbsFactory(抽象工廠)具體實現的工廠子類。程式設計師可以根據創建對象類型使用對應的工廠子類。這樣將單個的簡單工廠類變成了工廠簇,更利於程式碼的維護。
在這裡插入圖片描述

//一個抽象工廠模式的抽象層(介面)
public interface AbsFactory {
    //讓下面的工廠子類來具體實現
    Pizza createPizza(String orderType);
}
//這是一個工廠子類
public class BJFactory implements AbsFactory {

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("Cheess")) {
            pizza = new BJCheessPizza();
        }
        if (orderType.equals("Pepper")) {
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}
public class LDFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza=null;
        if (orderType.equals("Cheess")){
            pizza=new LDCheessPizza();
        }if (orderType.equals("Pepper")){
            pizza=new LDPepperPizza();
        }
        return pizza;
    }
}
public class OrderPizza {
    AbsFactory factory;

    //構造器
    public OrderPizza(AbsFactory factory) {
        setFactory(factory);
    }

    private void setFactory(AbsFactory factory) {
        Pizza pizza = null;
        String orderType = "";//用戶輸入
        this.factory = factory;
        do {
            orderType = getType();
            //factory可能是北京的工廠子類,也可能是倫敦的工廠子類
            pizza = factory.createPizza(orderType);
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.box();
                pizza.cut();
            } else {
                System.out.println("訂購失敗");
                break;
            }
        } while (true);
    }

    //寫一個方法,可以獲取客戶希望訂購的披薩種類
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
        new OrderPizza(new LDFactory());
    }
}

工廠模式小結

工廠模式的意義:將實例化對象的程式碼提取出來,放到一個類中統一管理和維護,達到和主項目的依賴關係的解耦,從而提高項目的擴展和維護性。

三種工廠模式:簡單工廠模式、工廠方法模式、抽象工廠模式。

設計模式的依賴抽象原則

  • 創建對象實例時,不要直接new類,而是把這個new類的動作放在一個工廠的方法中,並返回。有的書上說,變數不要直接持有具體類的引用;
  • 不要讓類繼承具體類,而是繼承抽象類或者是實現interface(介面);
  • 不要覆蓋基類中已經實現的方法。