0x01 前言
在編碼的時(shí)候倡勇,我們?yōu)榱藬U(kuò)展一個(gè)類常用的方法就是繼承山害,隨著擴(kuò)展功能越來越多如叼,子類會(huì)變得越來越膨脹冰木,
使系統(tǒng)變得不靈活。
裝飾器模式(Decorator pattern)允許像一個(gè)現(xiàn)有的對(duì)象添加新功能,同時(shí)又不改變其結(jié)構(gòu)踊沸,他能讓我們?cè)跀U(kuò)展類的同時(shí)歇终,系統(tǒng)保持較好的靈活性。
0x02 廢話不多說雕沿,進(jìn)入正題练湿,從一個(gè)場(chǎng)景開始
假設(shè)我們?cè)诒本┯幸粔K地,在這塊地上我們要蓋有好幾間房間的別墅审轮,每個(gè)房間的裝修費(fèi)用都不同肥哎,現(xiàn)在呢,我們需要對(duì)每個(gè)房間的費(fèi)用進(jìn)行計(jì)算并計(jì)算出別墅的價(jià)格疾渣。
//定義一個(gè)land表示這塊地
public interface Land {
// 定義蓋別墅需要花錢的規(guī)則
Integer cost();
}
那么land定義好了在這塊地上花錢的規(guī)則篡诽,但是蓋一間房需要多少錢呢?
此時(shí)我們定義一個(gè)Room類榴捡,這個(gè)類定義了一間房間需要建造的基本費(fèi)用杈女。
public class Room implements Land{
protected final int money = 1000 ;
@Override
public Integer cost() {
return money ;
}
}
開始建造房間,房間建造了兩間吊圾,一間客廳(LivingRoom)达椰,一間餐廳(DiningRoom),建造完之后發(fā)現(xiàn)偌大房間連件家具都沒有项乒,這時(shí)候啰劲,我們計(jì)算一間房成本的時(shí)候需要加上家具的價(jià)格,那么檀何,上代碼...
public class LivingRoom extends Room{
// 客廳
@Override
public Integer cost() {
return this.land.cost()+200;
}
}
public class DiningRoom extends Room{
// 餐廳
@Override
public void cost() {
return this.land.cost()+100;
}
}
所以機(jī)智如你很容易就得到了建造一間客廳和一間餐廳的花費(fèi)..
public static void main(String[] args) {
LivingRoom livingRoom = new LivingRoom();
System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
DiningRoom diningRoom = new DiningRoom();
System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
}
// 得到結(jié)果
建造一間客廳需要花費(fèi)1200
建造一間餐廳需要花費(fèi)1100
那么蝇裤,問題來了:挖掘機(jī)...(啪,好好說啊频鉴,挖掘機(jī)是什么鬼啊栓辜,喂)emmmmm,好接著說:
0x03 問題的產(chǎn)生
這么做實(shí)現(xiàn)確實(shí)很容易垛孔,雖然我們很容易就得到了建造一間客廳和一間餐廳需要的花費(fèi)藕甩,但是這樣的結(jié)構(gòu)不具備靈活性,如果....我是說如果我的地比較小不能同時(shí)建造兩間房子似炎,只能把客廳和餐廳建在同一個(gè)屋子里辛萍,那么該去怎么計(jì)算費(fèi)用?難道羡藐,還要很麻煩得去創(chuàng)建一個(gè)包含LivingDiningRoom類嗎贩毕?這樣做除了麻煩,而且還會(huì)使代碼重復(fù)仆嗦。
既然提出了問題辉阶,那么,肯定有解決的辦法,怎么解決呢谆甜?
欲知后事如何..(pia垃僚,知你妹啊,還來规辱?)谆棺,好吧,下面是解決辦法罕袋。
0x04 解決問題
(敲黑板8氖纭!)重點(diǎn)來了浴讯,為了更好的解決問題朵夏,我們需要做一些調(diào)整,同樣先聲明Land接口(抽象構(gòu)件榆纽,是具體構(gòu)件和抽象構(gòu)件的共同父類)和Room類( ConcreteComponent 仰猖,具體構(gòu)件,抽象構(gòu)件的子類奈籽,裝飾器可以額外添加職責(zé)饥侵。),不同的是引入房間的一個(gè)裝飾類RoomDecorator他實(shí)現(xiàn)了Land接口衣屏,因?yàn)闆]有實(shí)現(xiàn)Land的cost方法爆捞,所以需要聲明為抽象類,并且定義一個(gè)Land接口為參數(shù)的構(gòu)造函數(shù)勾拉,傳入對(duì)象會(huì)保存在land中,該屬性修飾符為protected方便子類訪問盗温。(劃重點(diǎn)藕赞,考試要考)
public abstract class RoomDecorator implements Land{
// 修飾符protected子類訪問
protected Land land ;
public RoomDecorator(Land land) { // 參數(shù)為實(shí)現(xiàn)Land接口的所有子類
this.land = land ;
}
}
我們重新定義客廳類和餐廳類
public class LivingRoom extends RoomDecorator{
public LivingRoom(Land land) {
super(land);
}
@Override
public Integer cost() {
return this.land.cost()+200;
}
}
public class DiningRoom extends RoomDecorator{
public DiningRoom(Land land) {
super(land);
}
@Override
public Integer cost() {
return this.land.cost()+100;
}
}
這兩個(gè)類都繼承了RoomDecorator,這意味著它們同時(shí)擁有指向Land接口的引用卖局,當(dāng)它們的cost()方法被調(diào)用時(shí)斧蜕,都會(huì)先調(diào)用實(shí)現(xiàn)了Land接口的子類的cost()方法,然后執(zhí)行自己特有的操作砚偶。
所以這時(shí)候建造一間客廳和一間餐廳所需的費(fèi)用是這么計(jì)算的:
public static void main(String[] args) {
LivingRoom livingRoom = new LivingRoom(new Room());
System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
DiningRoom diningRoom = new DiningRoom(new Room());
System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
}
// 輸出
建造一間客廳需要花費(fèi)1200
建造一間餐廳需要花費(fèi)1100
接著回到剛才的問題:建造一間有客廳又有餐廳的房間所需費(fèi)用批销,代碼如下
public static void main(String[] args) {
LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
System.out.println("建造一間客廳和餐廳需要花費(fèi)"+diningLivingRoom.cost());
}
// 輸出
建造一間客廳和餐廳需要花費(fèi)1300
我們計(jì)算建造費(fèi)用的思路是:我們計(jì)算一間房的基礎(chǔ)費(fèi)用-->在基礎(chǔ)房間裝飾為客廳的費(fèi)用-->在客廳的基礎(chǔ)上加上裝飾餐廳的費(fèi)用-->得到包含客廳餐廳房間的費(fèi)用,已經(jīng)不需要通過麻煩的創(chuàng)建LivingDiningRoom類來計(jì)算包含餐廳客廳房間的建造費(fèi)用了染坯。
這便是裝飾模式均芽,通過一層層的裝飾得到我們可以靈活的得到想要的結(jié)果,可以輕松地添加組件或者裝飾類來創(chuàng)建靈活的結(jié)構(gòu)单鹿。
0x05 完整代碼
//定義一個(gè)land表示這塊地
// Component:組件對(duì)象的接口掀宋,可以給這些對(duì)象動(dòng)態(tài)的添加職責(zé)
public interface Land {
// 定義蓋別墅需要花錢的規(guī)則
Integer cost();
}
// ConcreteComponent:具體的組件對(duì)象,實(shí)現(xiàn)了組件接口。該對(duì)象通常就是被裝飾器裝飾的原始對(duì)象劲妙,可以給這個(gè)對(duì)象添加職責(zé)湃鹊;
public class Room implements Land{
protected final int money = 1000 ;
@Override
public Integer cost() {
return money;
}
}
// Decorator:所有裝飾器的父類,需要定義一個(gè)與組件接口一致的接口
public abstract class RoomDecorator implements Land{
// 修飾符protected子類訪問
protected Land land ;
public RoomDecorator(Land land) {
this.land = land ;
}
}
// 裝飾類具體構(gòu)件
public class LivingRoom extends RoomDecorator{
public LivingRoom(Land land) {
super(land);
}
@Override
public Integer cost() {
return this.land.cost()+200;
}
}
// 裝飾類具體構(gòu)件
public class DiningRoom extends RoomDecorator{
public DiningRoom(Land land) {
super(land);
}
@Override
public Integer cost() {
return this.land.cost()+100;
}
}
// 測(cè)試
public class RoomTest{
public static void main(String[] args) {
LivingRoom livingRoom = new LivingRoom(new Room());
System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
DiningRoom diningRoom = new DiningRoom(new Room());
System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
System.out.println("建造一間客廳和餐廳需要花費(fèi)"+diningLivingRoom.cost());
}
}
0x06 The End...
Tips:在北京有塊地一輩子都不可能的镣奋,如果可以币呵,我愿意偷電動(dòng)車養(yǎng)你,點(diǎn)顆心在走嘛侨颈。