1. 程式人生 > >Java設計模式——工廠模式

Java設計模式——工廠模式

1. 什麼是工廠模式

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一,它提供了一種建立物件的最佳方式。

在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件

優點:

  1. 一個呼叫者想建立一個物件,只要知道其名稱就可以了。

  2. 擴充套件性高,如果想增加一個產品,只要擴充套件一個工廠類就可以。

  3. 遮蔽產品的具體實現,呼叫者只關心產品的介面。遮蔽產品的具體實現,呼叫者只關心產品的介面。

缺點:

每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。

工廠模式分為三種:簡單工廠模式工廠方法模式抽象工廠模式

這裡以製造coffee的例子開始工廠模式設計之旅。我們知道coffee只是一種泛舉,在點購咖啡時需要指定具體的咖啡種類:美式咖啡、卡布奇諾、拿鐵等等。

abstract class Coffee {
    public abstract String getName();
}

class Americano extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

class Cappuccino extends
Coffee { @Override public String getName() { return "卡布奇諾"; } } class Latte extends Coffee { @Override public String getName() { return "拿鐵"; } }

2. 簡單工廠模式

簡單工廠實際不能算作一種設計模式,它引入了建立者的概念,將例項化的程式碼從應用程式碼中抽離,在建立者類的靜態方法中只處理建立物件的細節,後續建立的例項如需改變,只需改造建立者類即可,但由於使用靜態方法來獲取物件,使其不能在執行期間通過不同方式去動態改變建立行為,因此存在一定侷限性。

class SimpleFactory {
    public static Coffee createInstance(String type) {
        if ("americano".equals(type)) {
            return new Americano();
        } else if ("cappuccino".equals(type)) {
            return new Cappuccino();
        } else if ("latte".equals(type)) {
            return new Latte();
        } else {
            throw new RuntimeException("type[" + type + "]型別不可識別,沒有匹配到可例項化的物件!");
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Coffee latte = SimpleFactory.createInstance("latte");
        System.out.println("建立的咖啡例項為:" + latte.getName());
        Coffee cappuccino = SimpleFactory.createInstance("cappuccino");
        System.out.println("建立的咖啡例項為:" + cappuccino.getName());
    }
}

/* output
建立的咖啡例項為:拿鐵
建立的咖啡例項為:卡布奇諾
*/

3. 工廠方法模式

定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個,工廠方法讓類把例項化推遲到了子類。

場景延伸:不同地區咖啡工廠受制於環境、原料等因素的影響,製造出的咖啡種類有限。中國咖啡工廠僅能製造卡布奇諾、拿鐵,而美國咖啡工廠僅能製造美式咖啡、拿鐵。

abstract class CoffeeFactory {
    public abstract Coffee[] createCoffee();
}

// 中國咖啡工廠
class ChinaCoffeeFactory extends CoffeeFactory {
    @Override
    public Coffee[] createCoffee() {
        return new Coffee[]{new Cappuccino(), new Latte()};
    }
}

// 美國咖啡工廠
class AmericaCoffeeFactory extends CoffeeFactory {
    @Override
    public Coffee[] createCoffee() {
        return new Coffee[]{new Americano(), new Latte()};
    }
}

public class Demo {
    public static void main(String[] args) {
        CoffeeFactory chinaCoffeeFactory = new ChinaCoffeeFactory();
        Coffee[] chinaCoffees = chinaCoffeeFactory.createCoffee();
        System.out.println("中國咖啡工廠可以生產的咖啡有:");
        print(chinaCoffees);

        CoffeeFactory americaCoffeeFactory = new AmericaCoffeeFactory();
        Coffee[] americaCoffees = americaCoffeeFactory.createCoffee();
        System.out.println("美國咖啡工廠可以生產的咖啡有:");
        print(americaCoffees);
    }

    static void print(Coffee[] c) {
        for (Coffee coffee : c) {
            System.out.println(coffee.getName());
        }
    }
}

/* output
中國咖啡工廠可以生產的咖啡有:
卡布奇諾
拿鐵
美國咖啡工廠可以生產的咖啡有:
美式咖啡
拿鐵
*/

4. 抽象工廠模式

提供一個介面,用於建立相關或依賴物件的家族,而不需要明確指定具體類。

在上述的場景上繼續延伸:咖啡工廠做大做強,引入了新的飲品種類:茶、 碳酸飲料。中國工廠只能製造咖啡和茶,美國工廠只能製造咖啡和碳酸飲料。

如果用上述工廠方法方式,除去對應的產品實體類還需要新增2個抽象工廠(茶製造工廠、碳酸飲料製造工廠),4個具體工廠實現。隨著產品的增多,會導致類爆炸。

所以這裡引出一個概念產品家族,在此例子中,不同的飲品就組成我們的飲品家族, 飲品家族開始承擔建立者的責任,負責製造不同的產品。

abstract class Drink {
    public abstract String getName();
}

abstract class Coffee extends Drink {}

abstract class Tea extends Drink {}

abstract class Sodas extends Drink {}

class Latte extends Coffee {
    @Override
    public String getName() {
        return "拿鐵";
    }
}

class MilkTea extends Tea {
    @Override
    public String getName() {
        return "奶茶";
    }
}

class CocaCola extends Sodas {
    @Override
    public String getName() {
        return "可口可樂";
    }
}

interface AbstractDrinksFactory {
    Coffee createCoffee();

    Tea createTea();

    Sodas createSodas();
}

class ChinaDrinksFactory implements AbstractDrinksFactory {
    @Override
    public Coffee createCoffee() {
        return new Latte();
    }

    @Override
    public Tea createTea() {
        return new MilkTea();
    }

    @Override
    public Sodas createSodas() {
        return null;
    }
}

class AmericaDrinksFactory implements AbstractDrinksFactory {
    @Override
    public Coffee createCoffee() {
        return new Latte();
    }

    @Override
    public Tea createTea() {
        return null;
    }

    @Override
    public Sodas createSodas() {
        return new CocaCola();
    }
}

public class Demo {
    public static void main(String[] args) {
        AbstractDrinksFactory chinaDrinksFactory = new ChinaDrinksFactory();
        Coffee coffee = chinaDrinksFactory.createCoffee();
        Tea tea = chinaDrinksFactory.createTea();
        Sodas sodas = chinaDrinksFactory.createSodas();
        System.out.println("中國飲品工廠有如下產品:");
        print(coffee);
        print(tea);
        print(sodas);

        AbstractDrinksFactory americaDrinksFactory = new AmericaDrinksFactory();
        coffee = americaDrinksFactory.createCoffee();
        tea = americaDrinksFactory.createTea();
        sodas = americaDrinksFactory.createSodas();
        System.out.println("美國飲品工廠有如下產品:");
        print(coffee);
        print(tea);
        print(sodas);
    }

    static void print(Drink drink) {
        if (drink == null) {
            System.out.println("產品:--");
        } else {
            System.out.println("產品:" + drink.getName());
        }
    }
}

/* output
中國飲品工廠有如下產品:
產品:拿鐵
產品:奶茶
產品:--
美國飲品工廠有如下產品:
產品:拿鐵
產品:--
產品:可口可樂
*/

5. 模式對比

如果產品單一,最合適用工廠模式,但是如果有多個業務品種、業務分類時,通過抽象工廠模式產生需要的物件是一種非常好的解決方式。

工廠方法模式 抽象工廠模式
針對的是一個產品等級結構 針對的是面向多個產品等級結構
一個抽象產品類 多個抽象產品類
可以派生出多個具體產品類 每個抽象產品類可以派生出多個具體產品類
一個抽象工廠類,可以派生出多個具體工廠類 一個抽象工廠類,可以派生出多個具體工廠類
每個具體工廠類只能建立一個具體產品類的例項 每個具體工廠類可以建立多個具體產品類的例項

參考資料: