一.模式定義
裝飾模式(Decorator Pattern) :動態地給一個對象增加一些額外的職責(Responsibility),就增加對象功能來說,裝飾模式比生成子類實現更爲靈活。
Decorator Pattern: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
二.模式要素
Component: 抽象構件
ConcreteComponent: 具體構件
Decorator: 抽象裝飾類
ConcreteDecorator: 具體裝飾類
三.舉例說明
大家應該都玩過王者榮耀,我們就以其中一個很熱的英雄孫悟空來舉例。
孫悟空有三個技能,一技能護身咒法,二技能鬥戰衝鋒,三技能如意金箍。
如果你只買了英雄而沒買皮膚的話,你的猴子是這樣的。
如果你買了至尊寶皮膚的話你的猴子是這樣的。
如果你買了地獄火皮膚的話你的猴子是這樣的。
沒有皮膚的猴子完全秀不起來,皮膚除了好看拉風以外,還有個重要的好處就是在局內釋放技能的時候的打擊效果更加酷炫了。
比如至尊寶的一、二技能都會觸發波若波羅密,三技能金箍棒也變成了紫色的大電燈管。
比如地獄火的一、二技能都是一根燃燒的棒子,三技能是一根粗大的火棒子咣的一聲插在地上。
想給孫悟空加皮膚、加特效,裝飾者模式正好可以用到。先畫一下UML圖如下:
首先是一個IHero接口,包括待實現的展示一、二、三技能的方法。
接着SunWukong作爲一個英雄實現IHero這個接口,展示屬於自己的三個技能。
接着有了皮膚系統後,Skin抽象類也先實現IHero接口,因爲對不同皮膚來說,三個技能的效果也是不一樣的。
最後具體的皮膚類,ZhiZunbao、HellFire繼承自Skin,在類中以構造器的方式傳入英雄。在三個展示技能的方法中額外加上各自皮膚的特效。
四.代碼實現
IHero.java
package decoratePattern;
/**
* @program: Test
* @description: 英雄接口
* @author: Lei Dong
* @create: 2019-04-21 12:10
**/
public interface IHero {
/**
* 展示一技能
*/
void showSkill1();
/**
* 展示二技能
*/
void showSkill2();
/**
* 展示三技能
*/
void showSkill3();
}
SunWukong.java
package decoratePattern;
/**
* @program: Test
* @description: 孫悟空
* @author: Lei Dong
* @create: 2019-04-21 12:09
**/
public class SunWukong implements IHero {
@Override
public void showSkill1() {
System.out.println("護身咒法");
}
@Override
public void showSkill2() {
System.out.println("鬥戰衝鋒");
}
@Override
public void showSkill3() {
System.out.println("如意金箍");
}
}
Skin.java
package decoratePattern;
/**
* @program: Test
* @description: Skin
* @author: Lei Dong
* @create: 2019-04-21 12:15
**/
public abstract class Skin implements IHero {
@Override
public abstract void showSkill1();
@Override
public abstract void showSkill2();
@Override
public abstract void showSkill3();
}
ZhiZunbao.java
package decoratePattern;
/**
* @program: Test
* @description: 至尊寶
* @author: Lei Dong
* @create: 2019-04-21 12:18
**/
public class ZhiZunbao extends Skin {
private IHero hero;
public ZhiZunbao(IHero hero) {
this.hero = hero;
}
@Override
public void showSkill1() {
System.out.print("至尊寶皮膚1技能特效 ");
hero.showSkill1();
}
@Override
public void showSkill2() {
System.out.print("至尊寶皮膚2技能特效 ");
hero.showSkill2();
}
@Override
public void showSkill3() {
System.out.print("至尊寶皮膚3技能特效 ");
hero.showSkill3();
}
}
HellFire.java
package decoratePattern;
/**
* @program: Test
* @description: 至尊寶
* @author: Lei Dong
* @create: 2019-04-21 12:18
**/
public class HellFire extends Skin {
private IHero hero;
public HellFire(IHero hero) {
this.hero = hero;
}
@Override
public void showSkill1() {
System.out.print("地獄火皮膚1技能特效 ");
hero.showSkill1();
}
@Override
public void showSkill2() {
System.out.print("地獄火皮膚2技能特效 ");
hero.showSkill2();
}
@Override
public void showSkill3() {
System.out.print("地獄火皮膚3技能特效 ");
hero.showSkill3();
}
}
Main.java
package decoratePattern;
/**
* @program: Test
* @description: Main
* @author: Lei Dong
* @create: 2019-04-21 12:04
**/
public class Main {
public static void main(String[] args) {
IHero hero = new SunWukong();
hero.showSkill1();
hero.showSkill2();
hero.showSkill3();
System.out.println("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~");
ZhiZunbao zhiZunbao = new ZhiZunbao(hero);
zhiZunbao.showSkill1();
zhiZunbao.showSkill2();
zhiZunbao.showSkill3();
System.out.println("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~");
HellFire hellFire = new HellFire(hero);
hellFire.showSkill1();
hellFire.showSkill2();
hellFire.showSkill3();
}
}
運行結果:
五.總結
1.裝飾者模式的優點
(1)裝飾模式與繼承關係的目的都是要擴展對象的功能,但是裝飾模式可以提供比繼承更多的靈活性。
(2)可以通過一種動態的方式來擴展一個對象的功能,通過配置文件可以在運行時選擇不同的裝飾器,從而實現不同的行爲。
(3)通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行爲的組合。可以使用多個具體裝飾類來裝飾同一對象,得到功能更爲強大的對象。
(4)具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合“開閉原則”。
2.裝飾者模式的缺點
(1)使用裝飾模式進行系統設計時將產生很多小對象,這些對象的區別在於它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,同時還將產生很多具體裝飾類。這些裝飾類和小對象的產生將增加系統的複雜度,加大學習與理解的難度。
(2)這種比繼承更加靈活機動的特性,也同時意味着裝飾模式比繼承更加易於出錯,排錯也很困難,對於多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較爲煩瑣。