一、引言
先看一個開發問題,很多人都玩過英雄聯盟這款遊戲:裡面有各種英雄,每個英雄都有各自的技能(一般是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;
}
@Override
public 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類就是用的裝飾者模式
為幫助開發者們提升面試技能、有機會入職BATJ等大廠公司,特别制作了這個專輯——這一次整體放出。
大緻内容包括了: Java 集合、JVM、多線程、并發程式設計、設計模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat等大廠面試題等、等技術棧!
歡迎大家關注頭條【java研習社】,回複【111】,擷取以上最新Java後端架構VIP學習資料以及視訊學習教程,然後一起學習,一文在手,面試我有。
每一個專欄都是大家非常關心,和非常有價值的話題,如果我的文章對你有所幫助,還請幫忙點贊、好評、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!