這是設計模式系列的第二篇,系列文章目錄以下:設計模式
幾乎全部的設計模式都是經過增長一層抽象來解決問題。this
上一篇中提到的三個設計模式經過相同的手段來達到相同的目的:它們經過接口和抽象方法來新增抽象層以應對變化。spa
這一系列的後續幾篇中會提到的四個設計模式經過相同的手段來達到不一樣的目的:它們經過新增一個類並持有原有類的方式實現對其擴展或限制。設計
這一篇先來看看裝飾者模式。代理
裝飾者模式就好像美顏相機,經過添加不一樣的裝飾品,它可讓你變成另外一個你。(雖然可能面目全非,但本質上仍是你)code
假設有四種飾品:耳環、鑽石、黃金、羽毛。不一樣裝飾品有不一樣價格,一般咱們會這樣作抽象:
//抽象飾品
public abstract class Accessory {
public abstract String name();//飾品名稱
public abstract int cost();//飾品價格
}
//耳環
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; }
}
複製代碼
現推出兩款新飾品:黃金耳環,羽毛黃金耳環。一樣的思路,使用繼承能夠解決問題:
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; }
}
複製代碼
GoldRing
和FeatherGoldRing
的價格,若是和黃金相關的飾品有好幾十個,那簡直是一場噩夢。GoldRing
價格的時候,咱們並無複用現有代碼,即沒有複用Gold
和Ring
已經定義的cost()
行爲,而只是經過繼承複用了類型(GoldRing
是一個Accessory
)。只複用類型而沒有複用行爲的後果是:當Gold
漲價時,GoldRing
無感知。有沒有一種比繼承更好的方案在現有飾品基礎上擴展新的飾品?
採用組合的方式就能夠實現既複用類型又複用行爲:
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();
}
}
複製代碼
Accessory
,但附加飾品還經過組合的方式持有一個超類型實例,這樣就能夠經過注入超類型的方式將其和任意基礎飾品組合到一塊兒造成新的飾品。用組合的方式實現羽毛黃金耳環:
Accessory ring = new Gold(new Feather(new Ring()));
複製代碼
Ring
做爲基礎飾品被Feather
裝飾成羽毛耳環,羽毛耳環接着被Gold
裝飾成換羽毛黃金耳環。FeatherGoldRing
的子類,而是複用了現有的Feather
和Gold
的行爲。這樣就解決了子類氾濫和控制力的問題。若是黃金漲價,只須要修改Gold.cost()
,全部被Gold
裝飾的飾品價格都會隨之而漲。Accessory ring = new Gold(new Gold(new Feather(new Ring())));
複製代碼
新的需求來了:基礎飾品鑲嵌附加飾品收取 10% 的一次性加工費。咱們能夠爲全部附加飾品增長一層抽象:
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
經過組合持有超類型Accessory
且規定了在構造時必須注入超類型,它還定義了鑲嵌加工費的收費標準。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();
}
}
複製代碼
使用裝飾者模式後,Gold
和Feather
中有一些樣板代碼,若是使用Kotlin
能夠將代碼簡化以下:
class Feather(val accessory: Accessory) : Accessory by accessory {
override fun name(): String = "Feather" + accessory.name()
override fun cost(): Int = 90 + accessory.cost()
}
class Gold(val accessory: Accessory) : Accessory by accessory {
override fun name(): String = "Gold" + accessory.name()
override fun cost(): Int = 300 + accessory.cost()
}
複製代碼
經過by
關鍵詞把類委託給一個具備超類型的成員變量accessory
,若是不重寫,類中的name()
和cost()
的默認實現都將轉發給accessory
。
運用組合的設計模式不止裝飾者一個,該系列的後續文章會繼續分析「組合」在設計模式中的運用。