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 設計模式》