裝飾者模式
動態的將責任附加到對象身上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案
問題
看一個飲料喝調料的問題
1,消費者需要一杯咖啡
2,消費者提出要求:要加糖
3,消費者提出要求:要加兩份牛奶
這個時候你會用什麼方式來解決呢?
解決思路
1,使用最原始的方法,記住每一種調料的價格和飲料的價格,最後+在一起。
- 如果消費者忽然不要某種飲料呢?
減去相應的價格
- 或者說想要發票?
。。。。
- 如果飲料種類非常多呢?
。。。。
怎麼辦呢?
2,使用裝飾者,把飲料看做爲被裝飾者,調料爲裝飾者。
如果消費者忽然不要某種飲料呢?
刪除對應的裝飾者
或者說想要發票?
每種調料內都添加描述即可
種類非常多
創建 package 進行分類,如 碳酸飲料:,分爲哪幾種(可樂,雪碧等在一個package),調料分爲哪幾種(加冰,薑汁等,在一個 package中)!
實現
1,首先定義飲料的抽象類
/**
* @name ModeDemo
* @class name:com.example.mode.decorate
* @author 345 QQ:1831712732
* @time 2020/1/6 21:29
* @description 飲料
*/
abstract class Beverage {
/**
* 未知的飲料
*/
var mDescription = "Unknown Beverage"
/**
* 獲取飲料描述
*/
open fun getDescription(): String {
return mDescription
}
/**
* 計算價格
*/
abstract fun cost(): Double
}
2,創建飲料
/**
* @name ModeDemo
* @class name:com.example.mode.decorate
* @author 345 QQ:1831712732
* @time 2020/1/6 21:36
* @description 被裝飾者:濃縮咖啡
*/
class Espresso : Beverage() {
init {
mDescription = "濃縮咖啡"
}
/**
* 濃縮咖啡的價格
*/
override fun cost(): Double {
return 32.00
}
}
/**
* @author 345 QQ:1831712732
* @name ModeDemo
* @class name:com.example.mode.decorate
* @time 2020/1/6 21:38
* @description 被裝飾者:混合咖啡
*/
class HouseBlend : Beverage() {
init {
mDescription = "混合咖啡"
}
override fun cost(): Double {
return 50.00
}
}
3,創建調料抽象類
/**
* @name ModeDemo
* @class name:com.example.mode.decorate
* @author 345 QQ:1831712732
* @time 2020/1/6 21:33
* @description 裝飾者類(調料),例如給咖啡添加牛奶,糖等,需要繼承此類
*/
abstract class CondimentDecorator : Beverage() {
/**
* 調料的描述
*/
abstract override fun getDescription(): String
}
4,創建具體的調料類
/**
* @name ModeDemo
* @class name:com.example.mode.decorate.seasoning
* @author 345 QQ:1831712732
* @time 2020/1/6 21:46
* @description 裝飾者:牛奶
*/
class Milk(private val beverage: Beverage) : CondimentDecorator() {
override fun getDescription(): String {
return beverage.getDescription() + ",牛奶"
}
/**
* 價格
*/
override fun cost(): Double {
return 5.00 + beverage.cost()
}
}
/**
* @name ModeDemo
* @class name:com.example.mode.decorate.seasoning
* @author 345 QQ:1831712732
* @time 2020/1/6 21:55
* @description 裝飾者:糖
*/
class Sugar(private val beverage: Beverage) : CondimentDecorator() {
/**
* 描述
*/
override fun getDescription(): String {
return beverage.getDescription() + ",糖"
}
/**
* 價格
*/
override fun cost(): Double {
return 4.00 + beverage.cost()
}
}
/**
* @name ModeDemo
* @class name:com.example.mode.decorate.seasoning
* @author 345 QQ:1831712732
* @time 2020/1/6 21:58
* @description 裝飾者:奶油
*/
class Whip(private val beverage: Beverage) : CondimentDecorator() {
override fun getDescription(): String {
return beverage.getDescription() + ",奶油"
}
/**
* jia價格
*/
override fun cost(): Double {
return 6.00 + beverage.cost()
}
}
5,測試
/**
* @name DesignModeDemo
* @class name:com.example.mode.decorate
* @author 345 QQ:1831712732
* @time 2020/1/6 22:00
* @description
*/
fun main() {
/**
* 一杯濃縮咖啡,加糖,牛奶
*/
val milk = Milk(Sugar(Espresso()))
println(milk.getDescription() + " ¥" + milk.cost())
/**
* 一杯杯混合咖啡,加牛奶,兩份奶油
*/
val whip = Whip(Whip(Milk(HouseBlend())))
println(whip.getDescription() + " ¥" + whip.cost())
}
結果
濃縮咖啡,糖,牛奶 ¥41.0
混合咖啡,牛奶,奶油,奶油 ¥67.0
解決問題
**1,**如果消費者忽然不要某種飲料呢?例如:兩份牛奶換成一份
這個比較麻煩了,需要修改代碼(一般情況不會這樣做,下面會給出解釋)。給調料抽象類添加如下代碼:
abstract class CondimentDecorator : Beverage() {
/**
* 調料的描述
*/
abstract override fun getDescription(): String
abstract fun setCondiment(condiment: CondimentDecorator)
}
實現類
class Whip(private var beverage: Beverage) : CondimentDecorator() {
override fun setCondiment(condiment: CondimentDecorator) {
beverage = condiment
}
override fun getDescription(): String {
return beverage.getDescription() + ",奶油"
}
/**
* 價格
*/
override fun cost(): Double {
return 6.00 + beverage.cost()
}
}
其他的實現類都是如此
測試
fun main() {
/**
* 一杯杯混合咖啡,加牛奶,兩份奶油
*/
val whip = Whip(Whip(Milk(HouseBlend())))
println(whip.getDescription() + " ¥" + whip.cost())
val houseBlend = HouseBlend()
val listOf = mutableListOf<CondimentDecorator>()
//兩份奶油,一份牛奶
listOf += Whip(houseBlend)
listOf += Whip(houseBlend)
listOf += Milk(houseBlend)
for (i in 0 until listOf.size) {
if (i < listOf.size - 1) {
listOf[i].setCondiment(listOf[i + 1])
}
}
println(listOf[0].getDescription() + " ¥" + listOf[0].cost())
//取消一個牛奶,這裏可以直接刪除引用
listOf.removeAt(0)
println(listOf[0].getDescription() + " ¥" + listOf[0].cost())
}
混合咖啡,牛奶,奶油,奶油 ¥67.0
混合咖啡,牛奶,奶油,奶油 ¥67.0
混合咖啡,牛奶,奶油 ¥61.0
看起來麻煩了一些。其實代碼還是很好理解的
原來都是通過構造方法進行裝飾,但是現在增加了一個 set 方法。通過 set 也可以進行裝飾
代碼還可以進行更好的優化
當然了,一般情況下也不需要這種代碼。因爲裝飾是一次性的。就像 JAVA 的 IO 一樣,也是用的是裝飾者模式,你不可能裝飾後在減掉某個裝飾板。這裏只是一個擴展
2,3
這兩個問題都差不多。只要添加相應的調料和飲料即可。
要點
- 裝飾者模式意味着一羣裝飾者類,這些類用於包裝具體的組件
- 你可以使用無數個裝飾者包裝一個組件
- 裝飾者會導致程序中出現過多的小對象。如果過度使用,會讓程序變得很複雜。
- 一旦裝飾後,一般情況下,就不能取消某個裝飾了。當然你可以使用我上面的做法,並做一下適當的修改。但是不推薦這種做法
參考自 Head First