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