引入案例——咖啡店點餐系統:
要求:
設計一個咖啡類(Coffee),并定義其兩個子類(美式咖啡【AmericanCoffee】和拿鐵咖啡【LatteCoffee】);再設計一個咖啡店類(CoffeeStore),咖啡店具有點咖啡的功能。
先看看沒有使用簡單工廠模式的實作
UML類圖如下:
Code如下:
(1)Main方法
package 簡單工場;
public class coffeeTest {
public static void main(String[] args) {
//1.建立咖啡店類
CoffeeStore store = new CoffeeStore();
//2.點咖啡
Coffee coffee = store.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
(2)Coffee類
package 簡單工場;
public abstract class Coffee {
public abstract String getName();
//加糖
public void addSugar(){
System.out.println("加糖");
}
//加奶
public void addMilk(){
System.out.println("加奶");
}
}
(3)AmericanCoffee類
package 簡單工場;
public class AmericanCoffee extends Coffee{
public String getName(){
return "美式咖啡";
}
}
(4)LatteCoffee類
package 簡單工場;
public class LatteCoffee extends Coffee{
public String getName(){
return "拿鐵咖啡";
}
}
(5)CoffeeStore類
package 簡單工場;
public class CoffeeStore {
public Coffee orderCoffee(String type){
//聲明Coffee類型的變量,根據不同類型建立不同的Coffee的子類對象
//初始化
Coffee coffee = null;
if ("amerian".equals(type)){
coffee = new AmericanCoffee();
}else if ("latte".equals(type)){
coffee = new LatteCoffee();
}else {
throw new RuntimeException("對不起,你所點咖啡沒有");
}
//加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
運作如下:
存在問題:
如果要更換一種coffee需要需改main中orderCoffee方法中的參數,這裡就兩個類,but有很多個類呢?顯然這就違背了設計模式七大原則中的開閉原則!
在java中,萬物皆對象,這些對象都需要建立,如果建立的時候直接new該對象,就會對該對象耦合嚴重,假如我們要更換對象,所有new對象的地方都需要修改一遍,這顯然違背了軟體設計的開閉原則。
如果我們使用工廠來生産對象,我們就隻和工廠打交道就可以了,徹底和對象解耦,如果要更換對象,直接在工廠裡更換該對象即可,達到了與對象解耦的目的;是以說,工廠模式最大的優點就是:解耦。
簡單工廠模式
簡單工廠不是一種設計模式,反而比較像是一種程式設計習慣。
簡單工廠包含如下角色:
- 抽象産品 :定義了産品的規範,描述了産品的主要特性和功能。
- 具體産品 :實作或者繼承抽象産品的子類
- 具體工廠 :提供了建立産品的方法,調用者通過該方法來擷取産品。
UML類圖:
代碼如下:
其中coffeeTest類,Coffee類,AmericanCoffee類,LatteCoffee類不用需改。
(1)添加一個類,SimpleCoffeeFactory
package 簡單工場;
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type){
Coffee coffee = null;
if ("amerian".equals(type)){
coffee = new AmericanCoffee();
}else if ("latte".equals(type)){
coffee = new LatteCoffee();
}else {
throw new RuntimeException("對不起,你所點咖啡沒有");
}
return coffee;
}
}
(2)将上面寫的CoffeeStore類修改一下
package 簡單工場;
public class CoffeeStore {
public Coffee orderCoffee(String type){
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
Coffee coffee = factory.createCoffee(type);
coffee.addSugar();
coffee.addMilk();
return coffee;
}
}
顯然,這樣子使用了SimpleCoffeeFactory類,在建立一個類的時候,就不會再依賴具體的産品對象了!
總結:
工廠(factory)處理建立對象的細節,一旦有了SimpleCoffeeFactory,CoffeeStore類中的orderCoffee()就變成此對象的客戶,後期如果需要Coffee對象直接從工廠中擷取即可。這樣也就解除了和Coffee實作類的耦合,同時又産生了新的耦合,CoffeeStore對象和SimpleCoffeeFactory工廠對象的耦合,工廠對象和商品對象的耦合。
後期如果再加新品種的咖啡,我們勢必要需求修改SimpleCoffeeFactory的代碼,違反了開閉原則。工廠類的用戶端可能有很多,比如建立美團外賣等,這樣隻需要修改工廠類的代碼,省去其他的修改操作。
優點:
封裝了建立對象的過程,可以通過參數直接擷取對象。把對象的建立和業務邏輯層分開,這樣以後就避免了修改客戶代碼,如果要實作新産品直接修改工廠類,而不需要在原代碼中修改,這樣就降低了客戶代碼修改的可能性,更加容易擴充。
缺點:
增加新産品時還是需要修改工廠類的代碼,違背了“開閉原則”。(雖然也是違背了開閉原則,但是比不使用直接建立對象要好得多!)
擴充:
在開發中也有一部分人将工廠類中的建立對象的功能定義為靜态的,這個就是靜态工廠模式,它也不是23種設計模式中的。代碼如下:
隻要需改一下SimpleCoffeeFactory即可(加一個static即可)
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffe;
}
}
在CoffeeStore中隻要修改一下調用靜态方法即可!
Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
package 簡單工場;
public class CoffeeStore {
public Coffee orderCoffee(String type){
//修改了這裡,不用建立SimpleCoffeeFactory類了
Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
coffee.addSugar();
coffee.addMilk();
return coffee;
}
}