摘要:這是設(shè)計(jì)模式系列的第二篇,系列文章目錄如下用一句話(huà)總結(jié)那些殊途同歸的設(shè)計(jì)模式工廠策略模版方法美顏相機(jī)中的設(shè)計(jì)模式裝飾者模式幾乎所有的設(shè)計(jì)模式都是通過(guò)增加一層抽象來(lái)解決問(wèn)題。
這是設(shè)計(jì)模式系列的第二篇,系列文章目錄如下:
用一句話(huà)總結(jié)那些殊途同歸的設(shè)計(jì)模式:工廠=?策略=?模版方法
美顏相機(jī)中的設(shè)計(jì)模式——裝飾者模式
幾乎所有的設(shè)計(jì)模式都是通過(guò)增加一層抽象來(lái)解決問(wèn)題。
上一篇中提到的三個(gè)設(shè)計(jì)模式通過(guò)相同的手段來(lái)達(dá)到相同的目的:它們通過(guò)接口和抽象方法來(lái)新增抽象層以應(yīng)對(duì)變化。
這一系列的后續(xù)幾篇中會(huì)提到的四個(gè)設(shè)計(jì)模式通過(guò)相同的手段來(lái)達(dá)到不同的目的:它們通過(guò)新增一個(gè)類(lèi)并持有原有類(lèi)的方式實(shí)現(xiàn)對(duì)其擴(kuò)展或限制。
這一篇先來(lái)看看裝飾者模式。
裝飾者模式就好像美顏相機(jī),通過(guò)添加不同的裝飾品,它可以讓你變成另一個(gè)你。(雖然可能面目全非,但本質(zhì)上還是你)
只復(fù)用類(lèi)型假設(shè)有四種飾品:耳環(huán)、鉆石、黃金、羽毛。不同裝飾品有不同價(jià)格,通常我們會(huì)這樣做抽象:
//抽象飾品
public abstract class Accessory {
public abstract String name();//飾品名稱(chēng)
public abstract int cost();//飾品價(jià)格
}
//耳環(huán)
public class Ring extends Accessory {
@Override
public String name() { return "Ring"; }
@Override
public int cost() { return 20; }
}
//鉆石
public class Diamond extends Accessory {
@Override
public String name() { return "Diamond"; }
@Override
public int cost() { return 1000; }
}
//黃金
public class Gold extends Accessory {
@Override
public String name() { return "Gold"; }
@Override
public int cost() { return 300; }
}
//羽毛
public class Feather extends Accessory {
@Override
public String name() { return "Feather"; }
@Override
public int cost() { return 90; }
}
現(xiàn)推出兩款新飾品:黃金耳環(huán),羽毛黃金耳環(huán)。同樣的思路,使用繼承可以解決問(wèn)題:
public class GoldRing extends Accessory {
@Override
public String name() { return "GoldRing"; }
@Override
public int cost() { return 320; }
}
public class FeatherGoldRing extends Accessory {
@Override
public String name() { "FeatherGoldRing"; }
@Override
public int cost() { return 1110; }
}
如果繼續(xù)推出更多的新品,比如羽毛耳環(huán),鉆石耳環(huán),羽毛鉆石耳環(huán)。。。每個(gè)新產(chǎn)品都用一個(gè)新的類(lèi)表示,這樣就會(huì)遇到子類(lèi)膨脹的問(wèn)題。
除此之外,繼承還有一個(gè)更致命的缺點(diǎn):對(duì)單個(gè)類(lèi)型的飾品沒(méi)有統(tǒng)一的控制力。如果黃金漲價(jià)了,我們需要分別修改GoldRing和FeatherGoldRing的價(jià)格,如果和黃金相關(guān)的飾品有好幾十個(gè),那簡(jiǎn)直是一場(chǎng)噩夢(mèng)。
在計(jì)算GoldRing價(jià)格的時(shí)候,我們并沒(méi)有復(fù)用現(xiàn)有代碼,即沒(méi)有復(fù)用Gold和Ring已經(jīng)定義的cost()行為,而只是通過(guò)繼承復(fù)用了類(lèi)型(GoldRing是一個(gè)Accessory)。只復(fù)用類(lèi)型而沒(méi)有復(fù)用行為的后果是:當(dāng)Gold漲價(jià)時(shí),GoldRing無(wú)感知。
有沒(méi)有一種比繼承更好的方案在現(xiàn)有飾品基礎(chǔ)上擴(kuò)展新的飾品?
既復(fù)用類(lèi)型又復(fù)用行為采用組合的方式就可以實(shí)現(xiàn)既復(fù)用類(lèi)型又復(fù)用行為:
public class Gold extends Accessory {
private Accessory accessory;
public Gold(Accessory accessory) { this.accessory = accessory; }
@Override
public String name() {
return "Gold " + accessory.name();
}
@Override
public int cost() {
return 300 + accessory.cost();
}
}
public class Feather extends Accessory {
private Accessory accessory;
public Feather(Accessory accessory) { this.accessory = accessory; }
@Override
public String name() {
return "Feather " + accessory.name();
}
@Override
public int cost() {
return 90 + accessory.cost();
}
}
上述四種飾品其實(shí)分為兩類(lèi),耳環(huán)屬于基本飾品,而羽毛、黃金、鉆石屬于附加飾品,附加飾品可以裝飾基本飾品。
附加飾品和基礎(chǔ)飾品擁有相同的超類(lèi)型Accessory,但附加飾品還通過(guò)組合的方式持有一個(gè)超類(lèi)型實(shí)例,這樣就可以通過(guò)注入超類(lèi)型的方式將其和任意基礎(chǔ)飾品組合到一起形成新的飾品。
用組合的方式實(shí)現(xiàn)羽毛黃金耳環(huán):
Accessory ring = new Gold(new Feather(new Ring()));
為了說(shuō)明裝飾與被裝飾的關(guān)系,使用了帶有俄羅斯套娃既視感的代碼(雖然這樣的代碼可讀性較差)。
Ring作為基礎(chǔ)飾品被Feather裝飾成羽毛耳環(huán),羽毛耳環(huán)接著被Gold裝飾成換羽毛黃金耳環(huán)。
過(guò)程中并沒(méi)有為羽毛黃金耳環(huán)新增一個(gè)叫FeatherGoldRing的子類(lèi),而是復(fù)用了現(xiàn)有的Feather和Gold的行為。這樣就解決了子類(lèi)泛濫和控制力的問(wèn)題。如果黃金漲價(jià),只需要修改Gold.cost(),所有被Gold裝飾的飾品價(jià)格都會(huì)隨之而漲。
這個(gè)方案還有一個(gè)更有用的好處:在運(yùn)行時(shí)動(dòng)態(tài)新增類(lèi)型。通過(guò)繼承新增的類(lèi)型都是在編譯時(shí)定死的,而通過(guò)組合的方式只要新增一行俄羅斯套娃式的代碼,程序運(yùn)行起來(lái)后就新增了一個(gè)類(lèi)型,比如要新增“雙倍黃金羽毛耳環(huán)”這個(gè)類(lèi)型,只需要如下的代碼:
Accessory ring = new Gold(new Gold(new Feather(new Ring())));抽象的裝飾者?
新的需求來(lái)了:基礎(chǔ)飾品鑲嵌附加飾品收取 10% 的一次性加工費(fèi)。我們可以為所有附加飾品增加一層抽象:
public abstract class Decorator extends Accessory{
private Accessory accessory;
public Decorator(Accessory accessory) { this.accessory = accessory; }
@Override
public int cost() {
return 1.1 * accessory.cost();
}
}
Decorator通過(guò)組合持有超類(lèi)型Accessory且規(guī)定了在構(gòu)造時(shí)必須注入超類(lèi)型,它還定義了鑲嵌加工費(fèi)的收費(fèi)標(biāo)準(zhǔn)。
現(xiàn)在就可以像這樣重新定義附加飾品:
public class Gold extends Decorator {
public Gold(Accessory accessory){ super(accessory); }
@Override
public String name() {
return "Gold " + accessory.name();
}
@Override
public int cost() {
return 300 + super.cost();
}
}
其實(shí)對(duì)于裝飾者模式來(lái)說(shuō),為裝飾者定義一個(gè)抽象的父類(lèi)不是必須的,只要滿(mǎn)足繼承超類(lèi)型,以及持有超類(lèi)型引用這兩點(diǎn)就是裝飾者模式。除非需要統(tǒng)一操作所有裝飾者,比如在美顏相機(jī)這個(gè)場(chǎng)景中,需要通過(guò)遍歷找出所有附加飾品。
總結(jié)裝飾者模式是一種復(fù)用原有類(lèi)并對(duì)其進(jìn)行擴(kuò)展的方式,它是繼承的替代方法。
裝飾者模式通過(guò)繼承原有類(lèi)型實(shí)現(xiàn)復(fù)用類(lèi)型。這一點(diǎn)很重要,因?yàn)樗惺褂迷蓄?lèi)型的地方不需要修改代碼就可以替換成裝飾者。
裝飾者模式通過(guò)組合持有原有類(lèi)實(shí)例實(shí)現(xiàn)復(fù)用行為。
裝飾者模式通過(guò)在調(diào)用原有類(lèi)方法的前后插入新的邏輯實(shí)現(xiàn)功能擴(kuò)展。
裝飾者模式符合開(kāi)閉原則,即在新增功能的時(shí)候沒(méi)有修改原有代碼。
裝飾者模式特別適用于子類(lèi)型之間可以有隨機(jī)組合的場(chǎng)景,比如美顏相機(jī)的各種道具組合之后形成新的道具。
運(yùn)用組合的設(shè)計(jì)模式不止裝飾者一個(gè),該系列的后續(xù)文章會(huì)繼續(xù)分析“組合”在設(shè)計(jì)模式中的運(yùn)用。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/7858.html
摘要:下裝飾者的實(shí)現(xiàn)了解了裝飾者模式和的概念之后,我們寫(xiě)一段能夠兼容的代碼來(lái)實(shí)現(xiàn)裝飾者模式原函數(shù)拍照片定義函數(shù)裝飾函數(shù)加濾鏡用裝飾函數(shù)裝飾原函數(shù)這樣我們就實(shí)現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動(dòng)上傳功能,也可以通過(guò)函數(shù)來(lái)添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當(dāng)我們拍了一張照片準(zhǔn)備發(fā)朋友...
摘要:設(shè)計(jì)模式系列之入門(mén)設(shè)計(jì)模式是一套被反復(fù)使用多數(shù)人知曉的經(jīng)過(guò)分類(lèi)編目的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。毫無(wú)疑問(wèn),設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的設(shè)計(jì)模式使代碼編制真正工程化設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。 PHP設(shè)計(jì)模式系列之入門(mén) 設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類(lèi)編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易...
閱讀 1169·2023-04-25 14:45
閱讀 2946·2021-09-30 09:59
閱讀 3223·2021-09-22 15:48
閱讀 2530·2019-08-30 15:55
閱讀 3665·2019-08-30 15:44
閱讀 652·2019-08-29 14:07
閱讀 3507·2019-08-26 13:45
閱讀 630·2019-08-26 11:31