摘要:這是設計模式系列的第二篇,系列文章目錄如下用一句話總結那些殊途同歸的設計模式工廠策略模版方法美顏相機中的設計模式裝飾者模式幾乎所有的設計模式都是通過增加一層抽象來解決問題。
這是設計模式系列的第二篇,系列文章目錄如下:
用一句話總結那些殊途同歸的設計模式:工廠=?策略=?模版方法
美顏相機中的設計模式——裝飾者模式
幾乎所有的設計模式都是通過增加一層抽象來解決問題。
上一篇中提到的三個設計模式通過相同的手段來達到相同的目的:它們通過接口和抽象方法來新增抽象層以應對變化。
這一系列的后續幾篇中會提到的四個設計模式通過相同的手段來達到不同的目的:它們通過新增一個類并持有原有類的方式實現對其擴展或限制。
這一篇先來看看裝飾者模式。
裝飾者模式就好像美顏相機,通過添加不同的裝飾品,它可以讓你變成另一個你。(雖然可能面目全非,但本質上還是你)
只復用類型假設有四種飾品:耳環、鉆石、黃金、羽毛。不同裝飾品有不同價格,通常我們會這樣做抽象:
//抽象飾品
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();
}
}
其實對于裝飾者模式來說,為裝飾者定義一個抽象的父類不是必須的,只要滿足繼承超類型,以及持有超類型引用這兩點就是裝飾者模式。除非需要統一操作所有裝飾者,比如在美顏相機這個場景中,需要通過遍歷找出所有附加飾品。
總結裝飾者模式是一種復用原有類并對其進行擴展的方式,它是繼承的替代方法。
裝飾者模式通過繼承原有類型實現復用類型。這一點很重要,因為所有使用原有類型的地方不需要修改代碼就可以替換成裝飾者。
裝飾者模式通過組合持有原有類實例實現復用行為。
裝飾者模式通過在調用原有類方法的前后插入新的邏輯實現功能擴展。
裝飾者模式符合開閉原則,即在新增功能的時候沒有修改原有代碼。
裝飾者模式特別適用于子類型之間可以有隨機組合的場景,比如美顏相機的各種道具組合之后形成新的道具。
運用組合的設計模式不止裝飾者一個,該系列的后續文章會繼續分析“組合”在設計模式中的運用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7858.html
摘要:下裝飾者的實現了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來實現裝飾者模式原函數拍照片定義函數裝飾函數加濾鏡用裝飾函數裝飾原函數這樣我們就實現了抽離拍照與濾鏡邏輯,如果以后需要自動上傳功能,也可以通過函數來添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當我們拍了一張照片準備發朋友...
摘要:設計模式系列之入門設計模式是一套被反復使用多數人知曉的經過分類編目的代碼設計經驗的總結。毫無疑問,設計模式于己于他人于系統都是多贏的設計模式使代碼編制真正工程化設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。 PHP設計模式系列之入門 設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易...
閱讀 1006·2023-04-25 14:45
閱讀 2780·2021-09-30 09:59
閱讀 3125·2021-09-22 15:48
閱讀 2428·2019-08-30 15:55
閱讀 3475·2019-08-30 15:44
閱讀 548·2019-08-29 14:07
閱讀 3416·2019-08-26 13:45
閱讀 541·2019-08-26 11:31