咖啡館:
一、咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(無因咖啡);
二、調料:Milk、Soy(豆漿)、Chocolate;
三、要求在擴展新的咖啡種類時,具備良好的擴展性、改動方便、維護方便;
四、使用OO的來計算不一樣種類咖啡的費用:客戶能夠點單品咖啡,也能夠單品咖啡+調料組合。java
一、Drink是一個抽象類,表示飲料;
二、description就是對咖啡的描述,好比咖啡的名字;
三、cost()方法就是計算費用,Drink類中作成一個抽象方法;
四、Decaf就是單品咖啡,繼承Drink,並實現cost;
五、Espress && Milk就是單品咖啡+調料,這個組合不少;
存在的問題是:這樣設計,會有不少類,當咱們增長一個單品咖啡,或者一個新的調料,類的數量就會倍增,就會出現類爆炸。ide
常規思路的解決方案由於咖啡單品+調料組合會形成類的倍增,所以能夠作改進,將調料內置到Drink類,這樣就不會形成類數量過多。從而提升項目的維護性(如圖):this
說明:milk,soy,chocolate能夠設計爲Boolean,表示是否要添加相應的調料。設計
將調料內置到Drink類中的解決方案能夠控制類的數量,不至於形成不少的類。可是,在增長或者刪除調料種類時,代碼的維護量很大。考慮到用戶能夠添加多份調料時,能夠將hasMilk返回一個對應int。3d
針對當前這個問題,能夠考慮使用裝飾者模式。code
一、裝飾者模式:動態的將新功能附加到對象上。在對象功能擴展方面,它比繼承更有彈性,裝飾者模式也體現了開閉原則(ocp);
二、這裏提到的動態的將新功能附加到對象和ocp原則,在後面的應用實例上會以代碼的形式體現。對象
一、裝飾者模式就像打包一個快遞
主體:好比:陶瓷、衣服(Component) //被裝飾者;
包裝:好比:報紙填充、塑料泡沫、紙板、木板(Decorator);
二、Component(主體):好比相似前面的Drink;blog
三、ConcreteComponent和Decorator
ConcreteComponent:具體的主體,好比前面的各個單品咖啡;繼承
Decorator:裝飾者,好比各調料;
四、在如圖的Component與ConcreteComponent之間,若是ConcreteComponent類不少,還能夠設計一個緩衝層,將共有的部分提取出來,抽象層一個類。遞歸
例如,裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack。
說明:
一、Milk包含了LongBlack;
二、一份Chocolate包含了(Milk+LongBlack);
三、一份Chocolate包含了(Chocolate+Milk+LongBlack);
這樣無論是什麼形式的單品咖啡+調料組合,經過遞歸方式能夠方便的組合和維護。
一、飲品抽象類
@Getter @Setter public abstract class Drink { public String des; // 描述 private float price = 0.0f; //計算費用的抽象方法 //子類來實現 public abstract float cost(); }
二、咖啡類
咖啡類:
public class Coffee extends Drink { @Override public float cost() { return super.getPrice(); } }
無因咖啡:
public class DeCaf extends Coffee { public DeCaf() { setDes(" 無因咖啡 "); setPrice(1.0f); } }
意大利咖啡:
public class Espresso extends Coffee { public Espresso() { setDes(" 意大利咖啡 "); setPrice(6.0f); } }
LongBlack:
public class LongBlack extends Coffee { public LongBlack() { setDes(" longblack "); setPrice(5.0f); } }
ShortBlack:
public class ShortBlack extends Coffee{ public ShortBlack() { setDes(" shortblack "); setPrice(4.0f); } }
三、裝飾者類
裝飾者父類:
public class Decorator extends Drink { private Drink obj; public Decorator(Drink obj) { //組合 this.obj = obj; } @Override public float cost() { // getPrice 本身價格 return super.getPrice() + obj.cost(); } @Override public String getDes() { // obj.getDes() 輸出被裝飾者的信息 return des + " " + getPrice() + " && " + obj.getDes(); } }
具體的裝飾類,巧克力:
//具體的Decorator, 這裏就是調味品 public class Chocolate extends Decorator { public Chocolate(Drink obj) { super(obj); setDes(" 巧克力 "); setPrice(3.0f); // 調味品 的價格 } }
具體的裝飾類,牛奶:
public class Milk extends Decorator { public Milk(Drink obj) { super(obj); setDes(" 牛奶 "); setPrice(2.0f); } }
具體的裝飾類,豆漿:
public class Soy extends Decorator{ public Soy(Drink obj) { super(obj); // TODO Auto-generated constructor stub setDes(" 豆漿 "); setPrice(1.5f); } }
四、咖啡店
public class CoffeeBar { public static void main(String[] args) { // 裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack // 1. 點一份 LongBlack Drink order = new LongBlack(); System.out.println("費用1=" + order.cost()); System.out.println("描述=" + order.getDes()); // 2. order 加入一份牛奶 order = new Milk(order); System.out.println("order 加入一份牛奶 費用 =" + order.cost()); System.out.println("order 加入一份牛奶 描述 = " + order.getDes()); // 3. order 加入一份巧克力 order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入一份巧克力 費用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes()); // 3. order 加入一份巧克力 order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入2份巧克力 費用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes()); System.out.println("==========================="); Drink order2 = new DeCaf(); System.out.println("order2 無因咖啡 費用 =" + order2.cost()); System.out.println("order2 無因咖啡 描述 = " + order2.getDes()); order2 = new Milk(order2); System.out.println("order2 無因咖啡 加入一份牛奶 費用 =" + order2.cost()); System.out.println("order2 無因咖啡 加入一份牛奶 描述 = " + order2.getDes()); } }