設計模式第三篇-裝飾者模式
一、引言
先看一個開發問題,很多人都玩過英雄聯盟這款遊戲:裡面有各種英雄,每個英雄都有各自的技能(一般是4個主動技能),每升一級可以升級一個技能,但是可升級的技能不固定。我們需要通過技能狀態來計算傷害,這個時候組合就非常多了(理論上是英雄數*技能數)。如果用繼承來解決的話,那麼子類就爆炸多了。
除了繼承還有一種設計,就是在基類上增加布爾變數,如Q,E等,然後提供一些has(get)和set方法來設定這些布林值,子類裡通過擴充套件計算傷害值,這個看起來是一個可行的設計,但這個設計也會有一些問題。
1.每個技能可以多次加點,單純靠布林值是處理不了的。
2.技能有可能會進行調整,那麼我們就必須通過修改基類來處理。
3.遊戲裡面還有裝備這種情況,增加裝備也是相當於多了技能(貌似用裝備來做例子更合適)。。。
我們用更好的方法來解決這個問題
這個問題的本質是擴充套件,我們想要擴充套件一些功能,但是不想用繼承。裝飾者模式可以解決這個問題
二、裝飾者模式
定義:動態地將責任附加到物件上。若要擴充套件功能,裝飾者提供了比繼承更有彈性的替代方案。
意圖:動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
主要解決:一般的,我們為了擴充套件一個類經常使用繼承方式實現,由於繼承為類引入靜態特徵,並且隨著擴充套件功能的增多,子類會很膨脹。
何時使用:在不想增加很多子類的情況下擴充套件類。
如何解決:將具體功能職責劃分,同時繼承裝飾者模式。
裝飾者通用類圖:
三、實現
英雄聯盟遊戲實現
//抽象基類 public abstract class Hero { //學習技能 public abstract void learnSkills(); } //具體英雄蘭博,需要被擴充套件的類 public class Lanbo extends Hero { //英雄屬性 private String name; public Lanbo(String name){ this.name = name; } @Overridepublic void learnSkills() { System.out.println(name + "學習了以上技能!"); } } //抽象技能類,裝飾者的抽象基類 public abstract class Skills extends Hero { private Hero hero; public Skills(Hero hero){ this.hero = hero; } @Override public void learnSkills() { if(hero!=null){ hero.learnSkills(); } } } //具體裝飾子類,用來裝飾 Q技能 public class Skill_Q extends Skills { private String skillName; public Skill_Q(Hero hero,String skillName) { super(hero); this.skillName=skillName; } @Override public void learnSkills() { System.out.println("學習了技能Q:" +skillName); super.learnSkills(); } } //具體裝飾子類,用來裝飾 W技能 public class Skill_W extends Skills { private String skillName; public Skill_W(Hero hero,String skillName) { super(hero); this.skillName=skillName; } @Override public void learnSkills() { System.out.println("學習了技能W:" +skillName); super.learnSkills(); } } //具體裝飾子類,用來裝飾E技能 public class Skill_E extends Skills { private String skillName; public Skill_E(Hero hero,String skillName) { super(hero); this.skillName=skillName; } @Override public void learnSkills() { System.out.println("學習了技能E:" +skillName); super.learnSkills(); } } //具體裝飾子類,用來裝飾R技能 public class Skill_R extends Skills { private String skillName; public Skill_R(Hero hero,String skillName) { super(hero); this.skillName=skillName; } @Override public void learnSkills() { System.out.println("學習了技能R:" +skillName); super.learnSkills(); } }
執行:
//選擇英雄 Hero hero = new Lanbo("蘭博"); Skills q = new Skill_Q(hero,"縱火盛宴"); Skills w = new Skill_W(q,"破碎護盾"); Skills e = new Skill_E(w,"電子魚叉"); Skills r = new Skill_R(e,"恆溫灼燒"); //學習技能 r.learnSkills();
執行結果:
四、總結
優點
- 裝飾這模式和繼承的目的都是擴充套件物件的功能,但裝飾者模式比繼承更靈活
- 通過使用不同的具體裝飾類以及這些類的排列組合,設計師可以創造出很多不同行為的組合
- 裝飾者模式有很好地可擴充套件性
缺點:裝飾者模式會導致設計中出現許多小物件,如果過度使用,會讓程式變的更復雜。並且更多的物件會是的差錯變得困難,特別是這些物件看上去都很像。
java.io類就是用的裝飾者模式
相關程式碼:https://gitee.com/yuanqinnan/pattern