1. 程式人生 > >JAVA設計模式之工廠模式—Factory Pattern

JAVA設計模式之工廠模式—Factory Pattern

1.工廠模式簡介

工廠模式用於物件的建立,使得客戶從具體的產品物件中被解耦。

2.工廠模式分類

這裡以製造coffee的例子開始工廠模式設計之旅。

我們知道coffee只是一種泛舉,在點購咖啡時需要指定具體的咖啡種類:美式咖啡、卡布奇諾、拿鐵等等。

/**
 * 
 * 拿鐵、美式咖啡、卡布奇諾等均為咖啡家族的一種產品
 * 咖啡則作為一種抽象概念
 * @author Lsj
 *
 */
public abstract class Coffee {

    /**
     * 獲取coffee名稱
     * @return
     */
    public abstract String getName();
    
}


/**
 * 美式咖啡
 * @author Lsj
 *
 */
public class Americano extends Coffee {

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

}


/**
 * 卡布奇諾
 * @author Lsj
 *
 */
public class Cappuccino extends Coffee {

    @Override
    public String getName() {
        return "卡布奇諾";
    }

}


/**
 * 拿鐵
 * @author Lsj
 *
 */
public class Latte extends Coffee {

    @Override
    public String getName() {
        return "拿鐵";
    }

}

2.1 簡單工廠

簡單工廠實際不能算作一種設計模式,它引入了建立者的概念,將例項化的程式碼從應用程式碼中抽離,在建立者類的靜態方法中只處理建立物件的細節,後續建立的例項如需改變,只需改造建立者類即可,

但由於使用靜態方法來獲取物件,使其不能在執行期間通過不同方式去動態改變建立行為,因此存在一定侷限性。

複製程式碼

/**
 * 簡單工廠--用於建立不同型別的咖啡例項
 * @author Lsj
 *
 */
public class SimpleFactory {
    
    /**
     * 通過型別獲取Coffee例項物件
     * @param type 咖啡型別
     * @return
     */
    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 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());
    }

}

2.2 工廠方法模式

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

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

/**
 * 定義一個抽象的咖啡工廠
 * @author Lsj
 */
public abstract class CoffeeFactory {
    
    /**
     * 生產可製造的咖啡
     * @return
     */
    public abstract Coffee[] createCoffee();

}


/**
 * 中國咖啡工廠
 * @author Lsj
 *
 */
public class ChinaCoffeeFactory extends CoffeeFactory {

    @Override
    public Coffee[] createCoffee() {
        // TODO Auto-generated method stub
        return new Coffee[]{new Cappuccino(), new Latte()};
    }

}


/**
 * 美國咖啡工廠
 * @author Lsj
 *
 */
public class AmericaCoffeeFactory extends CoffeeFactory {

    @Override
    public Coffee[] createCoffee() {
        // TODO Auto-generated method stub
        return new Coffee[]{new Americano(), new Latte()};
    }

}


/**
 * 工廠方法測試
 * @author Lsj
 *
 */
public class FactoryMethodTest {

    static void print(Coffee[] c){
        for (Coffee coffee : c) {
            System.out.println(coffee.getName());
        }
    }
    
    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);
    }
}

2.3 抽象工廠

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

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

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

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

/**
 * 抽象的飲料產品家族製造工廠
 * @author Lsj
 *
 */
public interface AbstractDrinksFactory {

    /**
     * 製造咖啡
     * @return
     */
    Coffee createCoffee();
    
    /**
     * 製造茶
     * @return
     */
    Tea createTea();
    
    /**
     * 製造碳酸飲料
     * @return
     */
    Sodas createSodas();
}


/**
 * 中國飲品工廠
 * 製造咖啡與茶
 * @author Lsj
 *
 */
public class ChinaDrinksFactory implements AbstractDrinksFactory {

    @Override
    public Coffee createCoffee() {
        // TODO Auto-generated method stub
        return new Latte();
    }

    @Override
    public Tea createTea() {
        // TODO Auto-generated method stub
        return new MilkTea();
    }

    @Override
    public Sodas createSodas() {
        // TODO Auto-generated method stub
        return null;
    }

}


/**
 * 美國飲品製造工廠
 * 製造咖啡和碳酸飲料
 * @author Lsj
 *
 */
public class AmericaDrinksFactory implements AbstractDrinksFactory {

    @Override
    public Coffee createCoffee() {
        // TODO Auto-generated method stub
        return new Latte();
    }

    @Override
    public Tea createTea() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Sodas createSodas() {
        // TODO Auto-generated method stub
        return new CocaCola();
    }

}


/**
 * 抽象工廠測試類
 * @author Lsj
 *
 */
public class AbstractFactoryTest {
    
    static void print(Drink drink){
        if(drink == null){
            System.out.println("產品:--" );
        }else{
            System.out.println("產品:" + drink.getName());
        }
    }
    
    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);
    }

}

3.總結

簡單工廠:不能算是真正意義上的設計模式,但可以將客戶程式從具體類解耦。

工廠方法:使用繼承,把物件的建立委託給子類,由子類來實現建立方法,可以看作是抽象工廠模式中只有單一產品的情況。

抽象工廠:使物件的建立被實現在工廠介面所暴露出來的方法中。

工廠模式可以幫助我們針對抽象/介面程式設計,而不是針對具體類程式設計,在不同的場景下按具體情況來使用。

參考書籍:

《HeadFirst 設計模式》