設(shè)計(jì)模式 - 深入淺出工廠模式

factory.jpg

工廠模式概述

工廠模式是設(shè)計(jì)模式的一種悔醋,從功能上來(lái)說(shuō)绸狐,它的主要作用是創(chuàng)建對(duì)象。細(xì)分一下鳞滨,有三種不同類(lèi)型的工廠模式洞焙,即,

  • 簡(jiǎn)單工廠模式
  • 工廠模式
  • 抽象工廠模式

本文使用swift編碼拯啦,試圖通過(guò)簡(jiǎn)單的代碼和配圖簡(jiǎn)要說(shuō)明工廠模式的使用澡匪。既然是關(guān)于工廠模式爽室,那么直接映射到現(xiàn)實(shí)世界凌那,就用汽車(chē)工廠生產(chǎn)汽車(chē)這樣一個(gè)模型來(lái)描述工廠模式的使用和逐步改進(jìn)的過(guò)程。

創(chuàng)建對(duì)象存在的問(wèn)題

這里假設(shè)镜豹,工廠可以生產(chǎn)汽車(chē)(Car)甫匹,這個(gè)數(shù)據(jù)模型可以用swift像下面這樣定義甸鸟,

protocol VehicleFeature {
    func speedUp() -> Void
    func numberOfWheelNumber() -> Int
}

class Vehicle: VehicleFeature {
    private var name: String? = "Vehicle"
    private var wheelNumber: Int?
        func speedUp() {
        print("car speed up")
    }
    
    func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

class Car: Vehicle {
    override func speedUp() {
        print("car speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

上面代碼定義了VehicleFeature協(xié)議,該協(xié)議中定義了-speedUp和-numberOfWheelNumber兩個(gè)方法兵迅,父類(lèi)Vehicle定義了name和wheelNumber兩個(gè)可選值屬性哀墓,并且Vehicle實(shí)現(xiàn)了VehicleFeature協(xié)議,子類(lèi)Car繼承父類(lèi)Vehicle喷兼。

這時(shí)客戶(hù)想要一輛汽車(chē)Car篮绰,直接調(diào)用Car的初始化方法let car = Car(),即可創(chuàng)建一個(gè)Car對(duì)象季惯。

想一想吠各,隨著工廠業(yè)務(wù)的擴(kuò)張,這時(shí)候還需要生產(chǎn)摩托車(chē)(MotorBicycle)和大巴車(chē)(Bus)勉抓,我們得新增加MotorBicycle和Bus兩個(gè)類(lèi)贾漏,如下代碼所示,

class MotorBicycle: Vehicle {
    
    override func speedUp() {
        print("motorBicycle speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}


class Bus: Vehicle {
    
    override func speedUp() {
        print("bus speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

通過(guò)let motorBicycle = MotorBicycle(); let bus = Bus()即可為客戶(hù)創(chuàng)建MotorBicycle和Bus對(duì)象藕筋。

在開(kāi)發(fā)過(guò)程中纵散,通過(guò)上述方式創(chuàng)建對(duì)象是一種很常見(jiàn)的方法,不過(guò)我們應(yīng)該意識(shí)到,隨著業(yè)務(wù)的調(diào)整和擴(kuò)展伍掀,類(lèi)似let car = Car()這樣的對(duì)象初始化方法會(huì)散落在項(xiàng)目的各個(gè)角落掰茶,代碼維護(hù)的成本越來(lái)越高,比如現(xiàn)在給父類(lèi)Vehicle添加一個(gè)-required init方法蜜笤,用以在初始化時(shí)候指定車(chē)輛(Vehicle)車(chē)輪數(shù)量濒蒋,代碼如下,

class Vehicle: VehicleFeature {
    private var name: String? = "Vehicle"
    private var wheelNumber: Int?
    
    required init(wheelNumber: Int) {
        self.wheelNumber = wheelNumber
    }
    
    func speedUp() {
        print("car speed up")
    }
    
    func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

在swift中把兔,如果父類(lèi)定義了-required init方法沪伙,那么子類(lèi)定義-init方法時(shí)必須要定義同樣的-required init方法,所以上述的Car县好、MotorBicycle和Bus三個(gè)子類(lèi)围橡,也要添加跟父類(lèi)Vehicle同樣的-required init方法,代碼如下缕贡,

class Car: Vehicle {
    
    required init(wheelNumber: Int) {
        super.init(wheelNumber: wheelNumber)
    }
    
    override func speedUp() {
        print("car speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

class MotorBicycle: Vehicle {
    
    required init(wheelNumber: Int) {
        super.init(wheelNumber: wheelNumber)
    }
    
    override func speedUp() {
        print("motorBicycle speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

class Bus: Vehicle {
    
    required init(wheelNumber: Int) {
        super.init(wheelNumber: wheelNumber)
    }
    
    override func speedUp() {
        print("bus speed up")
    }
    
    override func numberOfWheelNumber() -> Int {
        if let wheelNumber = self.wheelNumber {
            return wheelNumber
        }
        return 0
    }
}

經(jīng)過(guò)上面的更改某饰,我們需要通過(guò)帶參數(shù)的初始話方法let car = Car(wheelNumber: 4)let motorBicycle = MotorBicycle(wheelNumber: 2)來(lái)創(chuàng)建對(duì)象,項(xiàng)目中大量的創(chuàng)建對(duì)象的代碼得重新編碼和調(diào)整善绎。

簡(jiǎn)單工廠模式

上面的demo黔漂,簡(jiǎn)單說(shuō)明了創(chuàng)建對(duì)象時(shí)候可能存在的問(wèn)題,遇到這樣的問(wèn)題禀酱,我們?cè)撛趺磧?yōu)化呢炬守?

這時(shí)候簡(jiǎn)單工廠模式(Simple Factory Pattern)出現(xiàn)了,它可以短期內(nèi)緩解我們的壓力〖粮現(xiàn)在客戶(hù)需要一輛車(chē)Car减途,我們不再通過(guò)let car = ...來(lái)創(chuàng)建對(duì)象,而是將創(chuàng)建對(duì)象的任務(wù)交給簡(jiǎn)單工廠曹洽,這時(shí)需要在該模型中增加一個(gè)新的角色簡(jiǎn)單工廠(SimpleFactory)鳍置,它的主要功能是根據(jù)客戶(hù)輸入的機(jī)車(chē)類(lèi)型,為用戶(hù)創(chuàng)建對(duì)應(yīng)的機(jī)車(chē)送淆,下面的代碼展示了簡(jiǎn)單工廠模式的使用税产,

enum FactoryVehicleType {
    case CarType
    case MotorBicycle
    case BusType
}

class SimpleFactory {
    func createVehicle(vehicleType: FactoryVehicleType) -> Vehicle {
        switch vehicleType {
        case .CarType:
            return Car(wheelNumber: 4)
        case .MotorBicycle:
            return MotorBicycle(wheelNumber: 2)
        case .BusType:
            return Bus(wheelNumber: 4)
        }
    }
}

上述代碼,定義了枚舉類(lèi)型FactoryVehicleType偷崩,它描述了簡(jiǎn)單工廠可以創(chuàng)建的幾種車(chē)型辟拷,新增加的SimpleFactory(簡(jiǎn)單工廠類(lèi)),它包含-createVehicle方法阐斜,該方法根據(jù)輸入的FactoryVehicle枚舉值類(lèi)型衫冻,創(chuàng)建對(duì)應(yīng)的車(chē)型。

上述代碼存在了這樣幾個(gè)角色:抽象產(chǎn)品(Abstract Product)谒出、具體產(chǎn)品(Concrete Product)隅俘、簡(jiǎn)單工廠(SimpleFactory)以及客戶(hù)類(lèi)(Consumer)邻奠,其中SimpleFactory是簡(jiǎn)單工廠模式的核心類(lèi),它們之間的關(guān)系如下UML圖所示为居,

SimpleFactory.jpg

通過(guò)使用簡(jiǎn)單工廠方法碌宴,為我們創(chuàng)建對(duì)象提供了方便,也節(jié)省了開(kāi)發(fā)者的精力颜骤。假設(shè)現(xiàn)在改動(dòng)了父類(lèi)Vehicle的required init方法唧喉,各個(gè)子類(lèi)也要做相應(yīng)的改動(dòng)捣卤,此時(shí)我們只需在SimpleFactory的-createVehicle方法中改動(dòng)創(chuàng)建子類(lèi)的初始化方法即可忍抽,而項(xiàng)目各個(gè)角落中通過(guò)SimpleFactory創(chuàng)建子類(lèi)對(duì)象的零散的代碼都不需要做任何的改動(dòng),這無(wú)疑是一個(gè)大大的改進(jìn)董朝。

但是簡(jiǎn)單工廠模式也存在很大的不足鸠项,那就是隨著工廠規(guī)模的增加和客戶(hù)的需求,現(xiàn)在需要生產(chǎn)更多類(lèi)型的車(chē)輛子姜,比如現(xiàn)在要增加生產(chǎn)坦克(Tank)和SUV祟绊,這時(shí)候我們得重新調(diào)整SimpleFactory,代碼如下哥捕,

enum FactoryVehicleType {
    case CarType
    case MotorBicycle
    case BusType
    case TankType
    case SUVType
}

class SimpleFactory {
    func createVehicle(vehicleType: FactoryVehicleType) -> Vehicle {
        switch vehicleType {
        case .CarType:
            return Car(wheelNumber: 4)
        case .MotorBicycle:
            return MotorBicycle(wheelNumber: 2)
        case .BusType:
            return Bus(wheelNumber: 4)
        case .TankType:
              return Tank(wheelNumber: 16)
        case .SUVType:
              return SUV(wheelNumber: 4)
        }
    }
}

可以想象牧抽,隨著工廠的發(fā)展,SimpleFactory的代碼會(huì)越來(lái)越臃腫遥赚,每增加一條新型車(chē)輛的生產(chǎn)線扬舒,就得新增加FactoryVehicleType枚舉類(lèi)型的種類(lèi),同時(shí)更改SimpleFactory的-createVehicle方法凫佛。所以說(shuō)讲坎,SimpleFactory只能短期內(nèi)緩解工廠的壓力,這種方案并不是一個(gè)長(zhǎng)久之計(jì)愧薛。

這么說(shuō)來(lái)晨炕,SimpleFactory并不是像它的名字一樣看起來(lái)簡(jiǎn)單和簡(jiǎn)潔,這里的Simple也許僅僅代表了簡(jiǎn)單工廠模式適合處理相對(duì)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景毫炉。粗略地看一下臃腫不堪的SimpleFactory代碼瓮栗,我們可以說(shuō)SimpleFactory之所以臃腫不堪,是因?yàn)樗袚?dān)的職能太多了瞄勾,它要負(fù)責(zé)創(chuàng)建Car遵馆、Bus和MotorBicycle,還要負(fù)責(zé)創(chuàng)建Tank和SUV丰榴,在將來(lái)它可能還要承擔(dān)更多更繁雜的職能货邓,這顯然違背了軟件開(kāi)發(fā)中的“單一職責(zé)”和“開(kāi)閉原則”。

開(kāi)閉原則:Open-Closed Principle,OCP) "Software entities should be open for extension,but closed for modification"四濒。翻譯一下:“軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放换况,對(duì)修改關(guān)閉”职辨。通俗點(diǎn)來(lái)說(shuō)就是:軟件系統(tǒng)中包含的各種組件,例如模塊(Modules)戈二、類(lèi)(Classes)以及功能(Functions)等等舒裤,應(yīng)該在不修改現(xiàn)有代碼的基礎(chǔ)上,引入新功能觉吭。開(kāi)閉原則中“開(kāi)”腾供,是指對(duì)于組件功能的擴(kuò)展是開(kāi)放的,是允許對(duì)其進(jìn)行功能擴(kuò)展的鲜滩;開(kāi)閉原則中“閉”伴鳖,是指對(duì)于原有代碼的修改是封閉的,即不應(yīng)該修改原有的代碼徙硅。

怎樣優(yōu)化SimpleFactroy榜聂,其中的關(guān)鍵點(diǎn)和問(wèn)題所在其實(shí)就是SimpleFactory職責(zé)太多、代碼臃腫嗓蘑,這時(shí)我們需要對(duì)SimpleFactory的職能進(jìn)行拆分须肆,將它創(chuàng)建車(chē)輛的各條業(yè)務(wù)線拆分給不同的工廠,這催生了FactoryPattern(工廠模式)桩皿。

工廠模式

FactoryPattern的出現(xiàn)就是為了簡(jiǎn)化SimpleFactory豌汇,它也符合軟件開(kāi)發(fā)和設(shè)計(jì)中的單一職責(zé)原則。就像現(xiàn)實(shí)世界中一樣泄隔,現(xiàn)在已經(jīng)不可能存在這樣一個(gè)巨型的工廠拒贱,它需要負(fù)責(zé)生產(chǎn)各種類(lèi)型的汽車(chē),我們需要的是具有單一職能并且高度專(zhuān)業(yè)化的工廠梅尤,這種單一職責(zé)的工廠也是從SimpleFactory拆分而來(lái)柜思,那么對(duì)應(yīng)于上面的代碼demo,我們拆分SimpleFactory巷燥,對(duì)于已經(jīng)定義的Car赡盘、Bus、MotorBicycle等數(shù)據(jù)模型則不需要改變缰揪,我們只需要增加對(duì)應(yīng)的三個(gè)工廠類(lèi)和一個(gè)抽象工廠協(xié)議(AbstractFactory)陨享,如下代碼所示,

protocol AbstractFactory {
    func createVehicle() -> Vehicle
}

class CarFactory: AbstractFactory {
    func createVehicle() -> Vehicle {
        return Car(wheelNumber: 4)
    }
}

class BusFactory: AbstractFactory {
    func createVehicle() -> Vehicle {
        return Bus(wheelNumber: 4)
    }
}

class MotorBicycleFactory: AbstractFactory {
    func createVehicle() -> Vehicle {
        return MotorBicycle(wheelNumber: 2)
    }
}

AbstractFactory是一個(gè)協(xié)議钝腺,它定義了一個(gè)創(chuàng)建Vehicle的-createVehicle方法抛姑,實(shí)現(xiàn)了該協(xié)議的三個(gè)具體工廠類(lèi)(Concrete Factory)需要實(shí)現(xiàn)該協(xié)議中的-createVehicle方法,分別創(chuàng)建對(duì)應(yīng)的車(chē)輛艳狐。

現(xiàn)在分別創(chuàng)建一個(gè)Car定硝、Bus和MotorBicycle對(duì)象,我們可以用如下代碼實(shí)現(xiàn)毫目,

let car = CarFactory().createVehicle()
let bus = BusFactory().createVehicle()
let motorBicycle = MotorBicycleFactory().createVehicle()

這樣蔬啡,每一種類(lèi)型的車(chē)輛都有其對(duì)應(yīng)的工廠類(lèi)诲侮,當(dāng)我們需要?jiǎng)?chuàng)建一種類(lèi)型的車(chē)輛時(shí),只需調(diào)用其對(duì)應(yīng)的工廠類(lèi)的-createVehicle方法箱蟆。

備注:工廠類(lèi)沟绪,例如CarFactory是負(fù)責(zé)創(chuàng)建Car對(duì)象的,上面的代碼存在一點(diǎn)點(diǎn)的瑕疵空猜,那就是創(chuàng)建多個(gè)Car對(duì)象時(shí)绽慈,也通過(guò)CarFactory()多次創(chuàng)建CarFactory對(duì)象,但是它并不涉及到數(shù)據(jù)存儲(chǔ)和業(yè)務(wù)邏輯辈毯,這樣無(wú)疑消耗了額外的內(nèi)存空間坝疼,為了性能考慮,可以將CarFactory漓摩、BusFactory和MotorBicycleFactory定義為單例模式裙士。

可以看出入客,在工廠模式中管毙,有這樣幾個(gè)角色:抽象產(chǎn)品(Abstract Product)、具體產(chǎn)品(Concrete Product)桌硫、抽象工廠(Abstract Factory)夭咬、具體工廠(Concrete Factory)以及客戶(hù)(Consumer),AbstractFactory是工廠模式的核心铆隘,它們之間的關(guān)系如下圖UML所示卓舵,

FactoryPattern.jpg

與簡(jiǎn)單工廠模式相比,工廠模式再也不必?zé)烙谟纺[的代碼膀钠,如果需要制造新的機(jī)車(chē)類(lèi)型掏湾,比如現(xiàn)在需要?jiǎng)?chuàng)建坦克和SUV,為了滿(mǎn)足這樣的需求肿嘲,我們首先創(chuàng)建兩個(gè)繼承于Vehicle的兩個(gè)子類(lèi)Tank和SUV融击,接著分別創(chuàng)建Tank和SUV對(duì)應(yīng)的工廠類(lèi)TankFactory和SUVFactory,使用了這樣的方法雳窟,不會(huì)改動(dòng)原有的代碼結(jié)構(gòu)尊浪,這樣也遵循了“開(kāi)閉原則”。

工廠模式可以滿(mǎn)足大部分需求封救,但是拇涤,但是,重要的但是說(shuō)三遍誉结,但是鹅士,當(dāng)我們將產(chǎn)品細(xì)分為不同的等級(jí)結(jié)構(gòu),例如Car這樣一個(gè)產(chǎn)品可以細(xì)分為發(fā)動(dòng)機(jī)(Engine)惩坑、車(chē)門(mén)(Door)和電池(Battery)等幾個(gè)重要的等級(jí)結(jié)構(gòu)掉盅;而Car本身也有很多類(lèi)型嘱朽,例如奔馳和奧迪,這種情況下怔接,Car有兩種類(lèi)型搪泳,每一種Car都分別有其對(duì)應(yīng)使用的Engine, Door和Battery,如下表所列扼脐,

產(chǎn)品族 / 產(chǎn)品等級(jí) 奔馳 奧迪
發(fā)動(dòng)機(jī)Engine BenzEngine AudiEngine
車(chē)門(mén)Door BenzDoor AudiDoor
電池Battery BenzBattery AudiBattery

這樣細(xì)分下來(lái)岸军,為了使一輛車(chē)可以有效使用,必須保證所用的配件能夠匹配瓦侮,想象一下生產(chǎn)的AudiEngine給Benz用艰赞,BenzDoor給Audi用,真是畫(huà)面太美不敢看肚吏。這時(shí)候給我們生產(chǎn)產(chǎn)品也帶來(lái)了問(wèn)題方妖,因?yàn)楝F(xiàn)在有兩種車(chē)類(lèi)型 - Benz和Audi,每種車(chē)類(lèi)型有三個(gè)重要配件 - Engine罚攀、Door和Battery党觅,如果使用工廠模式生產(chǎn)配件,我們得創(chuàng)建6個(gè)對(duì)應(yīng)的工廠斋泄,即BenzEngineFactory, BenzDoorFactory, BenzBatteryFactory和AudiEngineFactory, AudiDoorFactory, AudiBatteryFactory杯瞻,這無(wú)疑是一個(gè)讓人無(wú)語(yǔ)的方案。所以說(shuō)這種情況下炫掐,工廠模式已經(jīng)滿(mǎn)足不了當(dāng)前的需求魁莉,我們需要進(jìn)行更高一層次的抽象,這時(shí)候“抽象工廠模式”應(yīng)運(yùn)而生募胃。

這里旗唁,筆者在討論生產(chǎn)時(shí)候偷換了概念,文章前段討論簡(jiǎn)單工廠模式(SimpleFactory)和工廠模式(FactoryPattern)時(shí)痹束,工廠生產(chǎn)的產(chǎn)品是Vehicle检疫,現(xiàn)在為了引申出抽象工廠模式(AbstractFactory)將Vehicle的子類(lèi)Car進(jìn)行了細(xì)分,工廠生產(chǎn)產(chǎn)品關(guān)注點(diǎn)不在是Vehicle参袱,而是著重討論生產(chǎn)Car相關(guān)配件 - Engine, Door和Battery电谣。

抽象工廠模式

在進(jìn)一步探討抽象工廠模式之前,我們有必要了解“產(chǎn)品族”和“產(chǎn)品等級(jí)”這兩個(gè)概念抹蚀,所謂產(chǎn)品族剿牺,是指位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中,功能相關(guān)聯(lián)的產(chǎn)品組成的家族环壤。比如奔馳的Engine, Door, Battery組成一個(gè)產(chǎn)品族晒来,Audi的Engine, Door, Battery組成一個(gè)產(chǎn)品族。而這兩個(gè)產(chǎn)品族都來(lái)自于三個(gè)產(chǎn)品等級(jí):Engine, Door和Battery郑现。

生產(chǎn)奔馳的Engine, Door和Battery必須匹配才能組合成有效使用的BenzCar湃崩;同樣生產(chǎn)Audi的Engine, Door和Battery也必須匹配才能組合成可用的AudiCar荧降。也許讓同一個(gè)工廠生產(chǎn)這些配件才能保證合理匹配,例如讓BenzFactory生產(chǎn)奔馳的Engine, Door和Battery攒读,讓AudiFactory生產(chǎn)Audi的Engine, Door和Battery朵诫,采用這樣的做法,我們也避免了工廠模式中為每一種類(lèi)型的配件創(chuàng)建對(duì)應(yīng)的工廠薄扁。既然BenzFactory和AudiFactory都是為了創(chuàng)建一個(gè)產(chǎn)品族剪返,這樣我們可以將BenzFactory和AudiFactory抽象出一個(gè)AbstractFactory,該抽象工廠有三個(gè)方法邓梅,-createEngine, -createDoor, -createBattery脱盲,

如下代碼所示,使用了抽象工廠模式實(shí)現(xiàn)了生產(chǎn)Car的配件日缨,

class Component { // 配件 - 翻譯不好見(jiàn)諒哈
    var price: Float = 0.0
    
}

class Engine: Component {
    var speed: Int = 0
    var weight: Float = 0.0
}

class BenzEngine: Engine {
    
}

class AudiEngine: Engine {
    
}

class Door: Component {
    var color: UIColor = UIColor.clearColor()
    var hasWindow: Bool = true
}

class BenzDoor: Door {

}

class AudiDoor: Door {

}


class Battery: Component {
    var workTime: Int = 0
    var lekeage: Bool = false //是否漏電
}

class BenzBattery: Battery {
    
}

class AudiBattery: Battery {
    
}

protocol AbstractCarFactory {
    func createEngine() -> Engine
    func createDoor() -> Door
    func createBattery() -> Battery
}

class BenzFactory: AbstractCarFactory {
    func createEngine() -> Engine {
        return BenzEngine()
    }
    
    func createDoor() -> Door {
        return BenzDoor()
    }
    
    func createBattery() -> Battery {
        return BenzBattery()
    }
}

class AudiFactory: AbstractCarFactory {
    func createEngine() -> Engine {
        return AudiEngine()
    }
    
    func createDoor() -> Door {
        return AudiDoor()
    }
    
    func createBattery() -> Battery {
        return AudiBattery()
    }
}

簡(jiǎn)單描述上面的代碼钱反,

  1. 定義了一個(gè)基類(lèi)Component,它代表一個(gè)汽車(chē)配件的模型匣距,它有一個(gè)Float類(lèi)型的price屬性面哥;
  2. 定義三個(gè)子類(lèi)Engine, Door, Battery,它們都繼承自Component墨礁,并且各自定自己的屬性幢竹,例如Battery子類(lèi)定義了workTime(電池工作時(shí)長(zhǎng))和lekeage(是否漏電)耳峦,它們屬于抽象產(chǎn)品(abstract product)的范疇恩静;
  3. 定義了Benz和Audi兩種車(chē)型Engine, Door, Battery的子類(lèi),分別是BenzEngine, BenzDoor, BenzBattery和AudiEngine, AudiDoor, AudiBattery蹲坷,它屬于具體產(chǎn)品(concrete product)的范疇驶乾;
  4. 定義核心角色AbstractCarFactory,它是一個(gè)Protocol循签,它內(nèi)部定義了-crateEngine, -createDoor, -createBattery三個(gè)方法级乐,分別用于創(chuàng)建Engine, Door, Battery抽象產(chǎn)品,抽象工廠并不創(chuàng)建具體的產(chǎn)品县匠;
  5. 定義BenzFactory和AudiFactory兩個(gè)具體工廠风科,它們實(shí)現(xiàn)AbstractCarFactory協(xié)議,負(fù)責(zé)創(chuàng)建具體的產(chǎn)品對(duì)象乞旦,也就是說(shuō)抽象工廠定義接口贼穆,具體工廠實(shí)現(xiàn)接口邏輯。

分析總結(jié)兰粉,抽象工廠模式與工廠模式一樣故痊,都包含如下4個(gè)角色,即玖姑,

  • 抽象工廠
  • 具體工廠
  • 抽象產(chǎn)品
  • 具體產(chǎn)品

區(qū)別在于愕秫,工廠模式只生產(chǎn)一種單一的產(chǎn)品慨菱,而抽象工廠模式生產(chǎn)多種類(lèi)型的產(chǎn)品,準(zhǔn)確來(lái)說(shuō)戴甩,抽象工廠模式可以生產(chǎn)一個(gè)產(chǎn)品族符喝。

它們之間的關(guān)系如下圖UML所示,

AbstractFactory.jpg

此時(shí)我們可以這樣創(chuàng)建一個(gè)Benz汽車(chē)對(duì)象甜孤,如下代碼所示洲劣,

class Car {
    var engine: Engine
    var door: Door
    var battery: Battery
    
    init(engine: Engine, door: Door, battery: Battery) {
        self.engine = engine
        self.door = door
        self.battery = battery
    }
}


func makeCar(carFactory: AbstractCarFactory) -> Car {
    let engine = carFactory.createEngine()
    let door = carFactory.createDoor()
    let battery = carFactory.createBattery()
    let car = Car(engine: engine, door: door, battery: battery)
    return car
}

let benzCar = makeCar(BenzFactory())

簡(jiǎn)單解釋上述代碼,首先新定義了一個(gè)類(lèi)Car课蔬,它包含Engine, Door, Battery三個(gè)屬性囱稽,并定義了-init初始化方法;接著定義了-makeCar方法二跋,它接受的參數(shù)是AbstractCarFactory战惊,返回一個(gè)Car對(duì)象;我們需要?jiǎng)?chuàng)建benzCar扎即,直接為-makeCar方法參數(shù)設(shè)置為BenzFactory對(duì)象即可吞获。

筆者冒泡:本文在兩臺(tái)不同電腦寫(xiě)作,因?yàn)橄到y(tǒng)原因谚鄙,Mac Book Pro不能使用ArgoUML各拷,所以只能換個(gè)方式畫(huà)UML類(lèi)圖,所以導(dǎo)致抽象工廠模式的UML圖和之前的畫(huà)風(fēng)不一樣闷营。因?yàn)闀r(shí)間有限烤黍,所以也重新畫(huà)圖,導(dǎo)致風(fēng)格不統(tǒng)一傻盟,忘讀者見(jiàn)諒速蕊。

Objective-C SDK中的抽象工廠模式

在Objecitve-C SDK中,NSNumber是我們經(jīng)常使用的類(lèi)娘赴,我們可以使用NSNumber創(chuàng)建不同類(lèi)型的數(shù)據(jù)规哲,如下代碼,

NSNumber *integerValue = [NSNumber numberWithInteger:1000];
NSNumber *boolValue = [NSNumber numberWithBool: YES];
...

實(shí)際上NSnumber是一個(gè)類(lèi)族诽表,大概就是文章前面提到的產(chǎn)品族這樣的概念吧唉锌,它包含Char, NSInteger, Bool, Short...等,如果沒(méi)有NSNumber提供的抽象工廠模式來(lái)創(chuàng)建對(duì)象竿奏,那么對(duì)于我們編碼來(lái)說(shuō)簡(jiǎn)直就是噩夢(mèng)袄简。

NSNumber本身就是一個(gè)抽象工廠,它提供了不同的接口用于創(chuàng)建不同類(lèi)型數(shù)據(jù)對(duì)應(yīng)的對(duì)象议双,至于創(chuàng)建對(duì)象的細(xì)節(jié)痘番,則交付給實(shí)現(xiàn)NSNumber抽象接口的具體工廠。

微信公眾號(hào)

歡迎關(guān)注本人微信公眾號(hào),請(qǐng)掃描下方二維碼汞舱,

foolishlion.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伍纫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昂芜,更是在濱河造成了極大的恐慌莹规,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泌神,死亡現(xiàn)場(chǎng)離奇詭異良漱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)欢际,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)母市,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人损趋,你說(shuō)我怎么就攤上這事患久。” “怎么了浑槽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵蒋失,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桐玻,道長(zhǎng)篙挽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任镊靴,我火速辦了婚禮铣卡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邑闲。我一直安慰自己算行,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布苫耸。 她就那樣靜靜地躺著,像睡著了一般儡陨。 火紅的嫁衣襯著肌膚如雪褪子。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天骗村,我揣著相機(jī)與錄音嫌褪,去河邊找鬼。 笑死胚股,一個(gè)胖子當(dāng)著我的面吹牛笼痛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缨伊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摘刑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起刻坊,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤枷恕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后谭胚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體徐块,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年灾而,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胡控。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旁趟,死狀恐怖铜犬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轻庆,我是刑警寧澤癣猾,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站余爆,受9級(jí)特大地震影響纷宇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛾方,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一像捶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桩砰,春花似錦拓春、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至煮纵,卻和暖如春懂鸵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背行疏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工匆光, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酿联。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓终息,卻偏偏與公主長(zhǎng)得像夺巩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子周崭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容