前言
最近看了一部英劇《黑鏡》,片中講述科技與人性碰撞可能帶來的一系列社會負影響,包括泛娛樂社會對底層人民精神的麻木作用、人工智能可能帶來的將短期痛苦無限延長等等影響,感慨萬分,在這里極力推薦大伙看看這部神劇。不扯了,下面開始聊聊裝飾者模式。
定義
先給出裝飾者模式的定義(來自維基百科):裝飾者模式,是面向對象編程領域中,一種動態地往一個類中添加新的行為的設計模式。就功能而言,修飾模式相比生成子類更為靈活,這樣可以給某個對象而不是整個類添加一些功能。裝飾者模式出現的意義是解決了運行時類功能的拓展,使得可以在運行時進行任一功能組合而不是需要為每種組合設計一個類(參見javaIO的設計)
相關設計原則
裝飾者模式遵循開放-關閉原則,及類應該對擴展開放,對修改關閉的原則。也就是說我們在項目開發過程中應盡量減少對已完成部分的修改,把重心放在功能的拓展至上。
基本結構
這里我們通過UML類圖來直觀地理解裝飾者模式的結構。如圖:
這里有幾個地方需要注意一下:裝飾者與被裝飾者繼承自同一個抽象類或者接口(Component)的原因:我們利用多態,通過將裝飾者與被裝飾者繼承自同一父抽象類或者接口實現被繼承者在被裝飾后類型不變的目的,也可以實現被裝飾者相互修飾的目的。第二個需要注意的是,每個裝飾者必須寫好它們的描述屬性,便于后期檢查維護。
實例
下面利用一個例子來講解裝飾者模式的使用方法。下面的例子純屬瞎扯我們的主角VinceBarry是一名NBA新秀,他想要提高自己的籃球能力,通過學習模仿一些巨星的技巧是他最終選擇的方案。于是他先學習了杜蘭特的跳投技巧,然后不過癮又學習了庫里的三分球和歐文的運球技巧,最終他的能力達到了130超過了2k的上限……扯遠了,下面我們利用代碼來為VinceBarry增加能力。
創建組件抽象類
首先我們需要一個父抽象類,這里我們命名為PlayBasketball。下面是他的具體實現:
abstract class PlayBasketball {
String description;
public String getDescription(){
return description;
}
public abstract int score();
}
這里我們為VinceBarry和技能們抽象出了兩個共有的屬性:描述和技能值。從這里也可以看出這個抽象類應該是裝飾者和被裝飾者共有屬性方法的容器。
創建裝飾者抽象類
下面我們再創建技能包的抽象類(對照上面的UML類圖),我們取名為BasketballSkill,下面貼上代碼:
abstract class BasketballSkill extends PlayBasketball {
public abstract String getDescription();
}
這個類繼承自父抽象類PlayBasketball,類中有一個方法用于描述不同的裝飾者,這個是裝飾者必須實現的方法。
創建各種裝飾者
接下來就是創建各種技能了,這里我創建了三個技能:CurryThreePoint,DurantJumpShoot,IrvingDribbling。由于后兩個代碼與第一個類似,我就只展示CurryThreePoint這項技能的代碼了。
class CurryThreePoint extends BasketballSkill {
private PlayBasketball playBasketball;
CurryThreePoint(PlayBasketball playBasketball) {
this.playBasketball = playBasketball;
}
@Override
public String getDescription() {
return playBasketball.getDescription()+" has curry's three point skill";
}
@Override
public int score() {
return 40 + playBasketball.score();
}
}
下面闡述一下這個類中的幾個關鍵點:首先是繼承自裝飾者父類,所以必須重寫getDescription()方法。然后我們重寫最關鍵的方法:score()。這個方法的特點是,將裝飾者中的特定行為與傳入的被裝飾者(也可能是裝飾者)的行為進行疊加,這里的裝飾者是我們在構造方法中傳入的Playbasketball類型的對象。通過這種方式實現了對象行為的修改或功能的拓展。也就是在這里我們為VinceBarry加上了庫里三分球的能力。
測試代碼
下面我們測試一下整個流程(關于裝飾者的構建,在工廠和生成器模式中有更加優秀的方案,日后再扯):
public class Court {
public static void main(String[] args) {
PlayBasketball vinceBarry = new VinceBarry();
System.out.println(vinceBarry.getDescription() + " " + vinceBarry.score());
vinceBarry = new DurantJumpShoot(vinceBarry);
vinceBarry = new CurryThreePoint(vinceBarry);
vinceBarry = new IrvingDribbling(vinceBarry);
System.out.println(vinceBarry.getDescription() + " " + vinceBarry.score());
}
}
輸出結果為
總結
又到一學期快結束的時間段了,想想近來自己也沒學啥,贈給自己一句話:就是干!