本文大部分內(nèi)容翻譯至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些許修改,并將代碼升級(jí)到了Swift2.0,翻譯不當(dāng)之處望多包涵。
復(fù)合模式(The Composite Pattern)
復(fù)合模式將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),它使得客戶對(duì)單個(gè)對(duì)象和復(fù)合對(duì)象的使用具有一致性。
示例工程
OS X Command Line Tool工程:
CarParts.swift
class Part {
let name:String
let price:Float
init(name:String, price:Float) {
self.name = name
self.price = price
}
}
class CompositePart {
let name:String
let parts:[Part]
init(name:String, parts:Part...) {
self.name = name
self.parts = parts;
}
}
我們定義了兩個(gè)汽車零件類用來修理汽車。Part類代表獨(dú)立的汽車零件,例如輪胎。 CompositePart 代表一些列組合而成的零件,例如包含輪胎的車輪。 CompositePart 包含了一個(gè)Part元素構(gòu)成的數(shù)組,用來代表構(gòu)成復(fù)合零件的各個(gè)獨(dú)立零件。
Orders.swift
import Foundation
class CustomerOrder {
let customer:String
let parts:[Part]
let compositeParts:[CompositePart]
init(customer:String, parts:[Part], composites:[CompositePart]) {
self.customer = customer
self.parts = parts
self.compositeParts = composites
}
var totalPrice:Float {
let total = parts.reduce(0){
subtotal,part -> Float in
return subtotal + part.price
}
return compositeParts.reduce(total){
subtotal,cpart -> Float in
return cpart.parts.reduce(subtotal){
tmptotal,tmpart -> Float in
return tmptotal + tmpart.price
}
}
}
func printDetails() {
print("Order for \(customer): Cost: \(formatCurrencyString(totalPrice))")
}
func formatCurrencyString(number:Float) -> String {
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
return formatter.stringFromNumber(number) ?? ""
}
}
CustomerOrder 類代表Part和CompositePart對(duì)象的一個(gè)訂單。
main.swift
let doorWindow = CompositePart(name: "DoorWindow", parts:
Part(name: "Window", price: 100.50),
Part(name: "Window Switch", price: 12))
let door = CompositePart(name: "Door", parts:
Part(name: "Window", price: 100.50),
Part(name: "Door Loom", price: 80),
Part(name: "Window Switch", price: 12),
Part(name: "Door Handles", price: 43.40))
let hood = Part(name: "Hood", price: 320)
let order = CustomerOrder(customer: "Bob", parts: [hood],
composites: [door, doorWindow])
order.printDetails()
運(yùn)行程序,得到下面輸出:
Order for Bob: Cost: ¥ 668.40
理解復(fù)合模式解決的問題
示例工程顯示了兩個(gè)不同的問題:
第一個(gè)問題是我們限制了零件的層級(jí)。當(dāng)我們創(chuàng)建一個(gè)CompositePart來代表車門時(shí),不得不去創(chuàng)建Part對(duì)象代表車窗和車窗開關(guān)。同時(shí)當(dāng)我們創(chuàng)建車門窗時(shí)又創(chuàng)建了相同的對(duì)象,雖然創(chuàng)建車門時(shí)已經(jīng)創(chuàng)建了。
第二個(gè)問題是操作零件的類需要知道CompositePart和Part的詳細(xì)構(gòu)成,這點(diǎn)我們可以從求總價(jià)的計(jì)算中看出:
......
var totalPrice:Float {
let total = parts.reduce(0){
subtotal,part -> Float in
return subtotal + part.price
}
return compositeParts.reduce(total){
subtotal,cpart -> Float in
return cpart.parts.reduce(subtotal){
tmptotal,tmpart -> Float in
return tmptotal + tmpart.price
}
}
}
......
理解復(fù)合模式
復(fù)合模式通過樹型方式和定義允許獨(dú)立和復(fù)合對(duì)象一致的協(xié)議來解決上述問題。

實(shí)現(xiàn)復(fù)合模式
首先是定義協(xié)議,協(xié)議是復(fù)合模式的心臟。
(1) 樹角色(Component):代表復(fù)合對(duì)象和單個(gè)對(duì)象共有特征的抽象,該角色是一個(gè)抽象類,它有一個(gè)對(duì)象列表,定義了一些增刪對(duì)象的操作?! ?br>
(2) 子樹角色(Composite):代表復(fù)合對(duì)象。樹上有很多子樹,子樹也是樹的一種?! ?br>
(3) 樹葉角色(Leaf):獨(dú)立對(duì)象,也就是Component中的操作的單個(gè)對(duì)象。樹葉是只一個(gè)結(jié)點(diǎn)的樹,也是特殊的一種樹。
CarParts.swift
protocol CarPart {
var name:String { get }
var price:Float { get }
}
class Part : CarPart {
let name:String
let price:Float
init(name:String, price:Float) {
self.name = name
self.price = price
}
}
class CompositePart : CarPart {
let name:String
let parts:[CarPart]
init(name:String, parts:CarPart...) {
self.name = name
self.parts = parts
}
var price:Float {
return parts.reduce(0){
total,part -> Float in
return total + part.price
}
}
}
接著我們應(yīng)用復(fù)合模式:
Orders.swift
import Foundation
class CustomerOrder {
let customer:String
let parts:[CarPart]
init(customer:String, parts:[CarPart]) {
self.customer = customer
self.parts = parts
}
var totalPrice:Float {
return parts.reduce(0){
total,part -> Float in
return total + part.price
}
}
func printDetails() {
print("Order for \(customer): Cost: \(formatCurrencyString(totalPrice))");
}
func formatCurrencyString(number:Float) -> String {
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
return formatter.stringFromNumber(number) ?? ""
}
}
最后,修改main.swift:
let doorWindow = CompositePart(name: "DoorWindow", parts:
Part(name: "Window", price: 100.50),
Part(name: "Window Switch", price: 12))
let door = CompositePart(name: "Door", parts:
doorWindow,
Part(name: "Door Loom", price: 80),
Part(name: "Door Handles", price: 43.40))
let hood = Part(name: "Hood", price: 320)
let order = CustomerOrder(customer: "Bob", parts: [hood, door, doorWindow])
order.printDetails()
可以看出我們上面創(chuàng)建CompositePart時(shí)直接用了doorWindow對(duì)象。運(yùn)行程序,輸出一下內(nèi)容:
Order for Bob: Cost: ¥ 668.40