若是在swift中循序漸進的談Gof設計模式,這在一開始就是錯誤的命題。緣由主要有兩個:編程
現在的swift的推薦編程範式並非面向對象,不少人都大談面向協議、函數式編程我就不展開了;現代的swift中有一些語法特性是當時的語言所不具有的,好比protocol extension,高階函數等。swift
因此本文將利用swift的語法來談下裝飾器模式在swift下的解決思路。設計模式
有人翻做裝飾器模式,也有翻成裝飾者模式,英文的名稱就是Decorator pattern。app
裝飾器模式可以實現動態的爲對象添加功能,是從一個對象外部來給對象添加功能。裝飾器模式就是基於對象組合的方式,能夠很靈活的給對象添加所須要的功能。編程語言
直接用代碼來講明。函數式編程
用Dish表示菜餚,一道菜有兩個屬性,名稱和價格。而後爲了方便測試重寫了description屬性,返回名稱和價格。函數
class Dish: CustomStringConvertible {
var name: String
var price: Int
init(name: String, price: Int) {
self.name = name
self.price = price
}
var description: String {
get {
return "\(name): \(price)元"
}
}
}複製代碼
再假設有一個菜的訂單對象,接收一個Dish對象後,最後能夠經過total方法返回這些菜的總價錢。具體就不實現了,大概這個邏輯。測試
class DishOrder {
func append(dish: Dish) {
}
func total() -> Int {
}
}複製代碼
假設在一個飯館裏,老闆發現這裏的客人點菜的時候喜歡讓廚師多放點鹽,你們都知道非典時期鹽很貴,因此老闆以爲很虧,決定若是一道菜多加鹽就要貴一塊錢。接着又來了一個需求,若是打包帶走,一道菜再加兩塊錢。若是咱們不能改變Dish的源碼(在實際項目中常會遇到這種狀況,可能這類定義在第三方的庫裏),要怎麼實現這兩個需求呢?spa
能夠定義兩個裝飾器,注意這兩個裝飾器都要繼承Dish:設計
// 加鹽的裝飾器
class SaltDishDecorator: Dish {
init(dish: Dish) {
super.init(name:"加糖 \(dish.name)", price: dish.price + 1)
}
}
// 外帶打包的裝飾器
class PackageDishDecorator: Dish {
init(dish: Dish) {
super.init(name:"打包 \(dish.name)", price: dish.price + 2)
}
}複製代碼
如今咱們要表示一道打包帶走的松鼠桂魚就這樣表示了:
let dish = PackageDishDecorator(dish: SaltDishDecorator(dish: Dish(name: "松鼠桂魚", price: 15)))複製代碼
這樣咱們就能夠給任意一道菜增長一些裝飾性的功能。也有人用咖啡舉例子,一杯咖啡可能要加糖,加奶,加巧克力等等。一層包一層。若是取名字的是中國人可能就叫洋蔥模式了。最後使用的時候行爲和Dish是同樣的。由於這些Decorator是繼承自Dish的。只是在初始化過程當中改變了原有的一些屬性。
編程時經常提到的一個指導思想就是組合優於繼承。
繼承最大的問題就在於你只能有一個爹。一個爹的結果就是能力有限,不夠靈活。因此最後還得認一些乾爹。
就拿上面的例子來說,如今是給Dish作了幾個裝飾功能,若是有一天說店裏的點心也要支持這兩個功能(給我來一個加鹽的饅頭!),是否是有要繼承點心類寫兩個裝飾器呢?
咱們能夠這麼理解這個需求,須要有這麼一個方法,接受一個Dish類型的參數,通過處理後返回一個Dish。咱們徹底能夠把這個方法經過extension寫在Dish身上。
extension Dish {
func salted() -> Dish {
return Dish(name:"加鹽 \(name)", price: price + 1)
}
func packaged() -> Dish {
return Dish(name:"打包 \(name)", price: price + 2)
}
}複製代碼
而後咱們就能夠鏈式調用:
let extenedDish = Dish(name: "松鼠桂魚", price: 15).salted().packaged()複製代碼
若是爲了未來的擴展靈活,也能夠把這個裝飾寫到protocol的extension裏。
protocol Product {
var name: String { get set }
var price: Int { get set }
}
protocol Salteable: Product {
func salted() -> Self
}複製代碼
上面先定義了一個產品的協議,有名稱和價格兩個屬性。 接着再定義了一個繼承Product的Salteable的協議。裏面有一個返回自身的salted方法。接着給這個protocol增長擴展實現:
extension Salteable {
func salted() -> Self {
var newProduct = self
newProduct.name = "加鹽 \(name)"
newProduct.price = price + 1
return newProduct
}
}複製代碼
而後咱們再定義一個表示小吃的Snack,和菜同樣也有兩個屬性。
struct Snack: CustomStringConvertible {
var name: String
var price: Int
init(name: String, price: Int) {
self.name = name
self.price = price
}
var description: String {
get {
return "\(name): \(price)元"
}
}
}複製代碼
若是咱們要給這個Snack增長加鹽的效果,只要聲明他實現Salteable協議就能夠了。
extension Snack: Salteable {
}複製代碼
這樣就夠啦! 看下輸出:
歡迎關注個人微博:@沒故事的卓同窗