我以訂披薩為例,給女朋友詳細講了Java設計模式的3種工廠模式

語言: CN / TW / HK
摘要:工廠模式是將例項化物件的程式碼提取出來,放到一個類中統一管理和維護,達到和主專案的依賴關係的解耦。從而提高專案的擴充套件和維護性。

本文分享自華為雲社群《【Java設計模式】用 披薩訂購案例 詳細講解三種工廠模式》,作者: 我是一棵捲心菜。

一、使用傳統方法

類圖

步驟概括

步驟一:建立一個Pizza抽象類

 public abstract class Pizza {
    public 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;
    }
}

分析: 這個類用來代表製作披薩的整個流程:準備階段prepare()烘烤階段bake()切割階段cut()打包階段box() ,假設各個披薩的準備階段需要的材料不一樣,所以把準備階段定義為一個抽象方法 ,其它三個階段都一樣。

步驟二:建立兩個披薩類

public class CheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("乳酪披薩正在準備中");
    }
}

分析: 這個類代表乳酪披薩,簡單重寫一下準備階段

 public class BeefPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("牛肉披薩正在準備中");
    }
}

分析: 這個類代表牛肉披薩,也簡單重寫一下準備階段

步驟三:制定訂購披薩類

 public class OrderPizza {
    public OrderPizza() {
        Pizza pizza = null;
        do {
            String pizzaType = getType();
            if ("cheese".equalsIgnoreCase(pizzaType)) {
                 pizza = new CheesePizza();
                 pizza.setName("cheese");
            } else if ("beef".equalsIgnoreCase(pizzaType)) {
                pizza = new BeefPizza();
                pizza.setName("beef");
            } else {
                break;
            }
            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 種類:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

分析: 慢慢看程式碼,可以明白,訂購披薩的邏輯程式碼寫在了該類的構造器中,getType()方法是用來獲取希望訂購的披薩種類。但是,如果我們需要新增新的披薩,就需要從這個類中繼續新增相應的邏輯語句,從而修改了訂購披薩的這個類,就違反了OCP原則

步驟四:建立執行類

 public class PizzaStore {
    public static void main(String[] args) {
       new OrderPizza();
    }
}

執行結果:

優缺點分析

  • 優點:比較好理解,簡單易操作
  • 缺點:違反了設計模式的ocp原則,即對擴充套件開放,對修改關閉。即當我們給類增加新功能的時候,儘量不修改程式碼,或者儘可能少修改程式碼

二、使用簡單工廠

類圖

基本介紹

  • 簡單工廠模式是屬於建立型模式,是工廠模式的一種。簡單工廠模式是由一個工廠物件決定創建出哪一種產品類的例項。簡單工廠模式是工廠模式家族中最簡單實用的模式
  • 簡單工廠模式定義了一個建立物件的類,由這個類來封裝例項化物件的行為(程式碼)
  • 在軟體開發中,當我們會用到大量的建立某種、某類或者某批物件時,就會使用到工廠模式

步驟概括

步驟一:建立簡單工廠

public class SimpleFactory {
    public static Pizza createPizza2(String orderType) {
        Pizza pizza = null;
        if ("beef".equalsIgnoreCase(orderType)) {
            pizza = new BeefPizza();
            pizza.setName(" beef ");
        } else if ("cheese".equalsIgnoreCase(orderType)) {
            pizza = new CheesePizza();
            pizza.setName("cheese");
        }
        return pizza;
    }
}

分析: 簡單工廠又叫做靜態工廠,我們寫一個靜態方法,可以方便後面程式碼的呼叫,這裡用到的類,跟用傳統方法用到的類一樣,沒有改變

步驟二:制定訂購披薩類

public class OrderPizza2 {
    public OrderPizza2() {
        do {
            String orderType = getType();
            Pizza pizza = SimpleFactory.createPizza2(orderType);
            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 種類:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

分析: 該類的構造器中用到了簡單工廠類SimpleFactory,這樣,我們就不需要在訂購披薩這一行為中去寫增加新的披薩的程式碼了,而是從工廠中寫新增新的披薩的程式碼,就不用再改動這個類。

執行結果:

優點分析

  • 使用簡單工廠模式來建立物件,更加的方便靈活,不需要修改訂購披薩的邏輯

三、使用工廠方法

新的需求

客戶在點披薩時,可以點不同口味的披薩,比如 北京的乳酪pizza、北京的胡椒pizza 或者是倫敦的乳酪pizza、倫敦的胡椒pizza

類圖

基本介紹

  • 工廠方法模式設計方案:將披薩專案的例項化功能抽象成抽象方法,在不同的口味點餐子類中具體實現。
  • 工廠方法模式:定義了一個建立物件的抽象方法,由子類決定要例項化的類。工廠方法模式將物件的例項化推遲到子類。

步驟概括

步驟一:建立四個披薩類

public class BJCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setName("北京的乳酪pizza");
        System.out.println("北京的乳酪pizza 準備原材料");
    }
}

分析: Pizza類跟上面的程式碼一樣,我就沒有再次寫了。此類是用來建立北京的乳酪口味的披薩

public class BJPepperPizza extends Pizza{
    @Override
    public void prepare() {
        setName("北京的胡椒pizza");
        System.out.println("北京的胡椒pizza 準備原材料");
    }
}

分析: 此類是用來建立北京的辣椒口味的披薩

public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setName("倫敦的乳酪pizza");
        System.out.println("倫敦的乳酪pizza 準備原材料");
    }
}

分析: 此類是用來建立倫敦的乳酪口味的披薩

public class LDPepperPizza extends Pizza {
    @Override
    public void prepare() {
        setName("倫敦的胡椒pizza");
        System.out.println("倫敦的胡椒pizza 準備原材料");
    }
}

分析: 此類是用來建立倫敦的辣椒口味的披薩

步驟二:建立訂購披薩抽象類

public abstract class OrderPizza {
    abstract Pizza createPizza(String orderType);
    public OrderPizza() {
        do {
            String orderType = getType();
            Pizza pizza = createPizza(orderType); //抽象方法,由工廠子類完成
            if (pizza == null){
                System.out.println("訂購披薩失敗");
                break;
            }
            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 種類:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

分析: 此類中定義一個抽象方法createPizza(), 讓各個工廠子類自己實現,構造器中寫訂購披薩的程式碼邏輯;getType()方法跟原來的沒有區別。

public class BJOrderPizza extends OrderPizza {
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}

分析: 此類用來繼承OrderPizza類,成為北京地區的訂購披薩分銷商

public class LDOrderPizza extends OrderPizza {
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new LDCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}

分析: 此類也用來繼承OrderPizza類,成為倫敦地區的訂購披薩分銷商

步驟三:建立執行類

public class PizzaStore {
    public static void main(String[] args) {
        String loc = "beijing";
        if (loc.equals("beijing")) {
            new BJOrderPizza();
        } else {
            new LDOrderPizza();
        }
    }
}

分析: 假設就是買北京地區的披薩

執行結果:

四、使用抽象工廠

類圖

基本介紹

  • 抽象工廠模式定義了一個interface用於建立相關或有依賴關係的物件簇,而無需指明具體的類
  • 抽象工廠模式可以將簡單工廠模式和工廠方法模式進行整合
  • 從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者稱為進一步的抽象)
  • 將工廠抽象成兩層,AbsFactory(抽象工廠) 和 具體實現的工廠子類。程式設計師可以根據建立物件型別使用對應的工廠子類。這樣將單個的簡單工廠類變成了工廠簇,更利於程式碼的維護和擴充套件。

步驟概括

步驟一:建立總工廠介面

public interface AbsFactory {
    public Pizza createPizza(String orderType);
}

分析: 此類是用來讓下面的工廠子類來具體實現

步驟二:建立分工廠

public class BJFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else 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("cheese")) {
            pizza = new LDCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}

分析: 這是工廠子類,用來製作倫敦的披薩

步驟三:建立訂購類

public class OrderPizza {
    private AbsFactory factory;
    public OrderPizza(AbsFactory factory) {
        setFactory(factory);
    }
    private void setFactory(AbsFactory factory) {
        do {
            this.factory = factory;
            String orderType = getType();
            // factory 可能是北京的工廠子類,也可能是倫敦的工廠子類
            Pizza pizza = factory.createPizza(orderType);
            if (pizza == null) { 
                System.out.println("訂購失敗");
                break;
            }
            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 種類:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

分析: Pizza pizza = factory.createPizza(orderType);這一語句主要運用的是java基礎裡面的多型,具體的實現功能交給實現其介面的子類

步驟四:建立執行類

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new LDFactory());
    }
}

分析: 假設買的是倫敦地區的披薩

執行結果:

總結

1、工廠模式的意義:

將例項化物件的程式碼提取出來,放到一個類中統一管理和維護,達到和主專案的依賴關係的解耦。從而提高專案的擴充套件和維護性。

2、三種工廠模式 (簡單工廠模式、工廠方法模式、抽象工廠模式)

3、設計模式的依賴抽象原則

建立物件例項時,不要直接 new 類, 而是把這個new 類的動作放在一個工廠的方法中,並返回
不要讓類繼承具體類,而是繼承抽象類或者是實現interface(介面)
不要覆蓋基類中已經實現的方法。

 

點選關注,第一時間瞭解華為雲新鮮技術~