每天一個設計模式之Decorator模式解決類間組合爆炸問題
模式動機
Decorator模式是一種相對簡單的物件結構性模式,動態和物件是個對應的關係,正如靜態和類這樣的對應關係,編譯時能夠決定的特質是靜態特質,動態則表示在執行時進行操作,傳統情況下使用的繼承是靜態的給類新增職責,動態的給物件新增職責,則是裝飾者模式所要完成的事。
給類和給物件新增職責有什麼不同呢
前者的靈活性要差,如果需要新增的職責很多,前者需要為每種情況都定義一個固定類,這裡的每種情況指的是職責的排列組合,假如我要為一個原始類新增5個職責,則會出現5的階乘種情況,不僅僅是類爆炸帶來的繁瑣,執行時對職責的修改是任意的,這就使得編譯時確定的類又要在執行時頻繁改變,而直接往物件
模式定義
動態的給一個物件新增一些職責,就增加功能來說,該模式比生成子類更為靈活 —— GOF官方定義
裝飾者模式是一種物件結構型模式,其別名也叫Wrapper(包裝器,如果我英語沒學錯的話),話不多說,進入本類文章套路的模式結構分析,見下面哦!
模式結構分析
裝飾模式包含如下角色:
1.Component(抽象構件)
抽象構件定義了物件的介面,可以給這些物件動態增加職責(方法)。抽象構件是具體構件和抽象裝飾類的共同父類,聲明瞭在具體構件中實現的業務邏輯方法。它的引入可以使客戶端以一致的方式處理未被裝飾的物件以及裝飾之後的物件,實現客戶端的透明操作;
2.ConcreteComponent(具體構件)
具體構件定義了具體構件物件,也就是具體的被裝飾的物品,實現了抽象構件中的宣告的方法,裝飾器可以給它增加額外的職責(方法),這是第一層裝飾
3.Decorator(我們滴主角,抽象裝飾類)
抽象裝飾類是抽象構建類的子類(思考,為啥要成為它的子類??),用於給具體構件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象構件物件的應用,也就是上圖的component,通過該引用可以呼叫裝飾之前構件物件的方法,並通過下面的子類擴充套件該方法,以達到裝飾的目的。
4.ConcreteDecorator(具體裝飾類)
具體裝飾類是Decorator的子類,負責向構件新增新的職責。每一個具體裝飾類都定義一些新的行為去裝飾接收進來的所有的父類為Component的子類物件
系不繫說的玄乎其乎的
其實!就是通過操作抽象類Component, 具體要被裝飾的構建類ConcreteComponent,Decorator也繼承了Component, 這樣做的目的何在呢?就是在具體要被裝飾的構建類ConcreteComponent每次被裝飾後,裝飾完的產物的父類也是Component,便於下一個裝飾品去接收進來,然後進行裝飾!(emmm…貌似很繞口,下面直接進入套路之擼程式碼吧,看完程式碼理解完再回頭來看上面的定義就會頭腦清醒甚至懷疑前幾秒自己怎麼那麼chun^_^)
應用場景介紹:
在某咖啡店,使用者可以在選擇茶或者咖啡作為基礎飲料,還可以任意新增紅糖、奶油、冰糖等料,請用裝.飾器模式實現該店飲料計費系統,以便能方便的計算不同型別的飲料價格。使用裝飾模式設計該系統,繪製類圖並程式設計實現。
//抽象構件Component
public interface Drink {
public void markDrinking();
public int getPrice();
}
package gz.kd.Decorater;
//具體構件類,也就是要被裝飾的類
public class Coffee implements Drink{
private int price;
public int getPrice() {
return price;
}
public Coffee(int price){
this.price=price;
}
@Override
public void markDrinking() {
// TODO Auto-generated method stub
System.out.println(" 製作Coffee ,現在"+getPrice() +"元");
}
}
//具體構件類,也就是要被裝飾的類
public class Tea implements Drink{
private int price;
public int getPrice() {
return price;
}
public Tea(int price){
this.price=price;
}
@Override
public void markDrinking() {
// TODO Auto-generated method stub
System.out.println(" 製作tea ,現在"+getPrice() +"元");
}
}
//Decorator抽象裝飾類
public class drinkDecorator implements Drink{
private Drink drink;
public drinkDecorator(Drink drink){
this.drink = drink;
}
@Override
public void markDrinking(){
drink.markDrinking();
}
@Override
public int getPrice() {
// TODO Auto-generated method stub
return drink.getPrice();
}
}
package gz.kd.Decorater;
//ConcreteDecorator(具體裝飾類)裝飾品,其實本身也是一個drink
public class Cream extends drinkDecorator{
public Cream(Drink drink) {
super(drink);
// TODO Auto-generated constructor stub
}
@Override
public void markDrinking() {
// TODO Auto-generated method stub
super.markDrinking();
System.out.println(" 加了奶油糖,現在"+getPrice()+"元");
}
@Override
public int getPrice() {
// TODO Auto-generated method stub
int num =super.getPrice()+1;
//System.out.println("price="+num);
return num;
}
}
public class IceSogar extends drinkDecorator{..此處省略
public class RedSogar extends drinkDecorator{..此處省略
package gz.kd.Decorater;
import java.util.Scanner;
import org.junit.Test;
public class client {
@Test
public void test(){
System.out.println("*************歡迎關顧kd奶茶店*************");
Drink drink = new Tea(2);
drink=new IceSogar(drink);
drink=new RedSogar(drink);
drink=new Cream(drink);
drink.markDrinking();
System.out.println("*************歡迎下次光臨*****************");
System.out.println("*************收賬人:kd 總金額:"+drink.getPrice()+"元********");
//System.out.println(drink.getPrice());
}
}
通過一個個drink傳遞到新的裝飾品中去,裝飾品中又繼承了Decorator,Decorator裡面又維護著一個Component, 保證了每次新的裝品都能接受Drink型別的物件,巧妙又優雅!