概述
定義
指在不改變現(xiàn)有對象結(jié)構(gòu)的情況下武花,動態(tài)地給該對象增加一些職責(zé)(即增加其額外功能)的模式圆凰。
看了網(wǎng)上的很多關(guān)于裝飾者模式的講解,都是一些什么關(guān)于咖啡算總價的例子体箕,起碼看的時候總是會把重心放到它的遞歸求總價上面专钉,而忘記了裝飾者的本質(zhì)是添加額外的功能。
剛好最近迷上了一款永劫無間的游戲累铅,個人覺得裝飾者模式似乎就像武器和魂玉之間的關(guān)系跃须。有了魂玉,武器就可以有一些特定的技能娃兽。我們以此為基礎(chǔ)來理解裝飾者模式菇民。
結(jié)構(gòu)
裝飾者(Decorator)模式中的角色:
- 抽象構(gòu)件(Component)角色 :定義一個抽象接口以規(guī)范準(zhǔn)備接收附加責(zé)任的對象。(各種武器的父類——Weapon)
- 具體構(gòu)件(Concrete Component)角色 :實(shí)現(xiàn)抽象構(gòu)件投储,通過裝飾角色為其添加一些職責(zé)第练。(具體武器——長槍)
- 抽象裝飾(Decorator)角色 : 繼承或?qū)崿F(xiàn)抽象構(gòu)件,并包含具體構(gòu)件的實(shí)例玛荞,可以通過其子類擴(kuò)展具體構(gòu)件的功能娇掏。(裝飾者)
- 具體裝飾(ConcreteDecorator)角色 :實(shí)現(xiàn)抽象裝飾的相關(guān)方法,并給具體構(gòu)件對象添加附加的責(zé)任勋眯。(特定技能——魂玉)
舉例
1婴梧、定義抽象構(gòu)件
這里使用抽象類或者接口都是可以的
public abstract class Weapon {
private String name;
public Weapon(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract String display();
}
2、定義具體構(gòu)件
這里可以定義多個具體武器客蹋,由于個人比較喜歡長槍志秃,所以只定義了一個長槍,繼承武器類嚼酝,并實(shí)現(xiàn)抽象方法
public class Pike extends Weapon{
public Pike() {
super("長槍");
}
@Override
public String display() {
return "長槍展示:";
}
}
3、定義裝飾者
裝飾者類為裝飾者模式的核心竟坛,它繼承武器類闽巩,并擁有一個構(gòu)件對象,可以認(rèn)為這個構(gòu)件對象才是武器本體担汤。在我看來涎跨,裝飾者是為了把魂玉‘’裝到“武器上,所以必須有一個武器本體崭歧。
public abstract class Decorator extends Weapon{
// 持有一個構(gòu)件對象
protected Weapon weapon;
public Decorator(String name,Weapon weapon) {
super(name);
this.weapon = weapon;
}
@Override
public String display() {
return weapon.display() + " 裝配: " + getName() + " 獲得能力: " + bisha();
}
// 對組件進(jìn)行裝飾的抽象方法
public abstract String bisha();
}
4隅很、具體裝飾角色
定義了具體的功能,比如這個魂玉率碾,使武器擁有了bisha叔营。
public class Jade1 extends Decorator{
public Jade1(Weapon weapon) {
super("魂玉1",weapon);
}
@Override
public String bisha() {
return "大圣游";
}
}
public class Jade2 extends Decorator{
public Jade2(Weapon weapon) {
super("魂玉2",weapon);
}
@Override
public String bisha() {
return "狂狼怒濤";
}
}
5屋彪、客戶端
public class AppTest {
public static void main(String[] args) {
Weapon pike = new Pike();
pike= new Jade1(pike);
pike= new Jade2(pike);
System.out.println(pike.display());
}
}
這里使用的時候有點(diǎn)類似javaIO流的使用,因?yàn)閖ava的IO流的設(shè)計(jì)就是使用了裝飾者模式绒尊。
長槍展示: 裝配: 魂玉1 獲得能力: 大圣游 裝配: 魂玉2 獲得能力: 狂狼怒濤
類圖
優(yōu)點(diǎn)
- 飾者模式可以帶來比繼承更加靈活性的擴(kuò)展功能畜挥,使用更加方便,可以通過組合不同的裝飾者對象來獲取具有不同行為狀態(tài)的多樣化的結(jié)果婴谱。裝飾者模式比繼承更具良好的擴(kuò)展性蟹但,完美的遵循開閉原則,繼承是靜態(tài)的附加責(zé)任谭羔,裝飾者則是動態(tài)的附加責(zé)任华糖。
- 裝飾類和被裝飾類可以獨(dú)立發(fā)展,不會相互耦合瘟裸,裝飾模式是繼承的一個替代模式客叉,裝飾模式可以動態(tài)擴(kuò)展一個實(shí)現(xiàn)類的功能。
源碼分析
如上面所說景描,裝飾者模式在javaIO流中應(yīng)用比較廣泛十办,像我們平常常見的BufferedInputStream就是一個具體裝飾者,它提供的功能就是添加緩沖區(qū)超棺,更好的讀取流的內(nèi)容向族。先來看一下BufferedInputStream的繼承關(guān)系
對比上面的類圖看這個關(guān)系是不是更加明顯呢?
Decorator就對應(yīng)著FilterInputStream
在FilterInputStream里也聚合了一個InputStream
再看看BufferedInputStream是如何使用的
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\bis.txt"));
byte[] bytes = new byte[1024];
int read = bis.read(bytes);
System.out.println(bytes+":"+read);
這里沒有直接new一個InputStream棠绘,而是new一個FileInpuStream件相,畢竟FileInputStream也是繼承InputStream。FileInputStream讀取在某些情況下是阻塞的氧苍,這里使用緩沖區(qū)來讀取既能提高讀取效率(因?yàn)榫彌_區(qū)在內(nèi)存里)夜矗,又能避免阻塞(BufferInputStream的read是非阻塞的)