創(chuàng)建型模式-抽象工廠

使用場景:繼承,創(chuàng)建各種子類對象结耀,多種繼承關系

意義:

隱藏了選擇子類、創(chuàng)建子類對象的過程匙铡,隱藏各對象間的相互關系图甜,簡化對外接口

原理描述:

可以看作是多個工廠方法生產(chǎn)出各種類型對象后,這些毫無關系的對象鳖眼,由抽象工廠方法聯(lián)系起來黑毅。

具體使用:

1、平常使用的方式

創(chuàng)建一個名為AbstractFactory的OS X命令行工具項目钦讳。如上面說的矿瘦,多種繼承關系,下面就創(chuàng)建各種不同的協(xié)議愿卒、和繼承協(xié)議的子類缚去。首先創(chuàng)建第一個協(xié)議及子類Floorplans.swift

//協(xié)議
protocol Floorplan {
    var seats:Int{get}
    var enginePosition:EngineOption {get}
}

enum EngineOption:String {
    case FRONT = "Front";
    case MID = "Mid";
}
//協(xié)議繼承的子類
class ShortFloorplan: Floorplan {
    var seats: Int = 2
    var enginePosition: EngineOption = EngineOption.MID
}

class StandardFloorplan: Floorplan {
    var seats: Int = 4
    var enginePosition: EngineOption = EngineOption.FRONT
}

class LongFloorplan: Floorplan {
    var seats: Int = 8
    var enginePosition: EngineOption = EngineOption.FRONT
}

第二個協(xié)議及子類Suspension.swift

protocol Suspension { 
    var suspensionType:SuspensionOption{get}
}

enum SuspensionOption:String {
   case STANDARD = "Standard";
   case SPORTS = "Firm";
   case SOFT = "Soft";
}

class RoadSuspension: Suspension {
    var suspensionType = SuspensionOption.STANDARD
}

class OffRoadSuspension: Suspension {
    var suspensionType: SuspensionOption = SuspensionOption.SOFT
}

class RaceSuspension: Suspension {
    var suspensionType: SuspensionOption = SuspensionOption.SPORTS
}

再創(chuàng)建第三個協(xié)議及子類Drivetrain.swift

protocol Drivetrain {
    var driveType:Driveoption{get}
}

enum Driveoption :String {
    case FRONT="Front";
    case REAR = "Rear";
    case ALL = "4WD";
}

class FrontWheelDrive: Drivetrain {
    var driveType: Driveoption = Driveoption.FRONT
}

class RearWheelDrive: Drivetrain {
    var driveType: Driveoption = Driveoption.REAR
}

class ALLWheelDrive: Drivetrain {
    var driveType: Driveoption = Driveoption.ALL
}

為了讓三個協(xié)議之間有聯(lián)系,創(chuàng)建CarsParts.swift

enum Cars:String {
    case COMPACT = "VM Golf";
    case SPORTS = "Porsche Boxter";
    case SUV = "Cadillac Escalade";
}
//由三種協(xié)議子類對象琼开、Cars枚舉易结,組成結構體Car
struct Car {
    var carType:Cars
    var floor:Floorplan
    var suspension:Suspension
    var drive:Drivetrain
    //提供各種屬性的打印
    func pritDetails(){
        print("Car type:\(carType.rawValue)")
        print("Seats: \(floor.seats)")
        print("Engine:\(floor.enginePosition.rawValue)")
        print("Susension:\(suspension.suspensionType.rawValue)")
        print("Drive:\(drive.driveType.rawValue)")
    }
}

main.swift中使用是這樣的:

var car = Car(carType:Cars.SPORTS,floor:ShortFloorplan(),suspension:RaceSuspension(),drive:RearWheelDrive())
car.pritDetails()
問題:創(chuàng)建結構體對象car時,用到了每個協(xié)議的具體實現(xiàn)方法,這樣暴露出去的壞處在于搞动,實現(xiàn)方法改變后躏精,car的初始化方法也會改變。我們想滋尉,可不可以提供一個隱藏所有選擇細節(jié)的方法玉控?類似于這樣var car = Car(carType:Cars.SPORTS)
2飞主、實現(xiàn)抽象工廠
  • 創(chuàng)建抽象工廠類:
    創(chuàng)建Abstract.swift
    提供創(chuàng)建每個類型的方法狮惜,沒有實現(xiàn),由子類重寫實現(xiàn)
class CarFatory{

    func createFloorplan() -> Floorplan {
        fatalError("Not implemented")
    }
    
    func creatSuspension() -> Suspension {
        fatalError("Not implemented")
    }
    
    func creatDrivetrain() -> Drivetrain {
        fatalError("Not implemented")
     }
  }
  • 具體工廠類
    創(chuàng)建Concrete.swift
    繼承抽象工廠碌识,重寫創(chuàng)建方法碾篡,實現(xiàn)每個類型具體創(chuàng)建
class CompactCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return StandardFloorplan()
    }
    override func creatSuspension() -> Suspension {
        return RoadSuspension()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return FrontWheelDrive()
    }
}

class SportsCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return ShortFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return RaceSuspension()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

class SUVCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return LongFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return OffRoadSuspension()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

抽象工廠類應該是漏了一個選擇的邏輯,根據(jù)類型選擇合適的工廠方法筏餐。于是在Abstract.swift需要添加如下:

 final class func getFactory(car:Cars) -> CarFatory?{
    
        var factoryType:CarFatory?
        switch car {
        case .COMPACT:
            factoryType = CompactCarFactory()
        case .SPORTS:
            factoryType = SportsCarFactory()
        case .SUV:
            factoryType = SUVCarFactory()
        }
        return factory
    }

于是main.swift現(xiàn)在使用起來是這樣的:

let factory = CarFatory.getFactory(car: Cars.SPORTS)

if (factory != nil){
    let car = Car(carType:Cars.SPORTS,floor:factory.createFloorplan(),suspension:factory.creatSuspension(),drive:factory.creatDrivetrain())
    car.pritDetails()
}

現(xiàn)在雖然沒有暴露選擇細節(jié)开泽,但是調用組件的細節(jié)暴露出來,干脆把調用組件也隱藏起來魁瞪。

于是在CarParts.swift中添加init方法把調用組件的細節(jié)加進去穆律。

enum Cars:String {
    case COMPACT = "VM Golf";
    case SPORTS = "Porsche Boxter";
    case SUV = "Cadillac Escalade";
}

struct Car {
    var carType:Cars
    var floor:Floorplan
    var suspension:Suspension
    var drive:Drivetrain
   //把調用組件的細節(jié)放在初始化過程 
    init(carType:Cars) {
        let concreteFactory = CarFatory.getFactory(car: carType)
        self.floor = (concreteFactory?.createFloorplan())!
        self.suspension = (concreteFactory?.creatSuspension())!
        self.drive = (concreteFactory?.creatDrivetrain())!
        self.carType = carType
    }
    
    
    func pritDetails(){
        print("Car type:\(carType.rawValue)")
        print("Seats: \(floor.seats)")
        print("Engine:\(floor.enginePosition.rawValue)")
        print("Susension:\(suspension.suspensionType.rawValue)")
        print("Drive:\(drive.driveType.rawValue)")
    }
}

至此抽象工廠已經(jīng)完整實現(xiàn),我們捋一捋思路:

1导俘、在干什么:

創(chuàng)建一個車car,car必須由三部分組成:Floorplan峦耘、SuspensionDrivetrain旅薄。而這三部分都有各自的子類辅髓,通過不同的子類組合就有了carType。那么抽象工廠解決的就是誰去創(chuàng)建這三部分子類少梁、誰去組裝子類洛口。

2、該怎么干:

大體分兩個步驟凯沪,一個抽象工廠類第焰、一個具體工廠類。抽象工廠類提供創(chuàng)建子類的方法妨马,由具體工廠類繼承實現(xiàn)挺举;抽象工廠還有一個對外提供選擇的接口,根據(jù)外界條件生成具體工廠類身笤。

屏幕快照 2017-03-31 00.32.03.png

以下為選讀

3豹悬、抽象工廠和其他模式結合的場景:
3.1、結合單例模式:

首先改動一下抽象工廠Abstract.swift

class CarFatory{
    
    //避免下面創(chuàng)建factoryType.init()報錯
    required init() {
    
    }
    
    func createFloorplan() -> Floorplan {
        fatalError("Not implemented")
    }
    
    func creatSuspension() -> Suspension {
        fatalError("Not implemented")
    }
    
    func creatDrivetrain() -> Drivetrain {
        fatalError("Not implemented")
    }
    
    final class func getFactory(car:Cars) -> CarFatory?{
        //使用元類
        var factoryType:CarFatory.Type
        switch car {
        case .COMPACT:
            factoryType = CompactCarFactory.self
        case .SPORTS:
            factoryType = SportsCarFactory.self
        case .SUV:
            factoryType = SUVCarFactory.self
        }
        //取得單例類型
        var factory = factoryType.sharedInstance
        //如果這個類沒有實現(xiàn)單例液荸,就init()返回對象即可
        if (factory == nil) {
            factory = factoryType.init()
        }
        return factory
    }
    //子類可以實現(xiàn)這個方法瞻佛,獲取單例
    class var sharedInstance:CarFatory? {
        get{
            return nil
        }
    }
}

然后改動具體工廠Concrete.swift


class CompactCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return StandardFloorplan()
    }
    override func creatSuspension() -> Suspension {
        return RoadSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return FrontWheelDrive()
    }
    //重寫父類方法,實現(xiàn)單例
    override class var sharedInstance:CarFatory? {
        get{
            struct SingletonWrapper{
                static let singleton = CompactCarFactory()
            }
            return SingletonWrapper.singleton
        }
    }
}

class SportsCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return ShortFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return RaceSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

class SUVCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return LongFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return OffRoadSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

3.2、結合原型模式:

這里原型模式主要用在具體實現(xiàn)類里伤柄,具體實現(xiàn)類里都用到枚舉作為返回值绊困。因為Swift的枚舉不能實現(xiàn)原型模式的NSCopying協(xié)議,所以就需要用到OC的枚舉橋接過來使用适刀。這里只對Suspension做改變秤朗。

  • 創(chuàng)建OC枚舉
屏幕快照 2017-03-31 00.57.15.png
屏幕快照 2017-03-31 00.57.27.png

第一次創(chuàng)建會生成橋接文件,點擊Yes就可以笔喉,橋接文件的作用取视,就是引入OC頭文件即可讓Swift使用。

#import "SuspensionOption.h"

SuspensionOption.h中的枚舉是這樣寫的:

typedef NS_ENUM(NSUInteger, SuspensionOption) {
   SuspensionOptionSTANDARD,
   SuspensionOptionSPORTS,
   SuspensionOptionSOFT
};

然后在修改Suspension.swift

//加入@objc常挚,這樣枚舉就能實現(xiàn)原型模式
@objc protocol Suspension{
    var suspensionType:SuspensionOption{get}
}

class RoadSuspension: Suspension {
    var suspensionType = SuspensionOption.STANDARD
}

class OffRoadSuspension: Suspension {
    var suspensionType: SuspensionOption = SuspensionOption.SOFT
}

//只對RaceSuspension實現(xiàn)copy功能
class RaceSuspension:NSObject,NSCopying,Suspension{
   
    var suspensionType: SuspensionOption = SuspensionOption.SPORTS
    
    func copy(with zone: NSZone? = nil) -> Any {
        return RaceSuspension()
    }
}

注意:必須在實現(xiàn)類中使用原型模式作谭,而不是在具體工廠中。
原因有兩點:
1奄毡、除非具體工廠是單例折欠,否則具體工廠使用原型模式會導致出現(xiàn)多個原型對象。

2吼过、哪個實現(xiàn)類為原型锐秦,哪個為實例,將散落在工廠類中盗忱。

為了避免這兩點酱床,Suspension.swift做如下修改:

//加入@objc,這樣枚舉就能實現(xiàn)原型模式
@objc protocol Suspension{
    var suspensionType:SuspensionOption{get}
    //為了統(tǒng)一copy方法售淡,不讓copy方法暴露在工廠方法中
    //添加這個方法統(tǒng)一封裝斤葱,不讓工廠方法知道是否實現(xiàn)copy
    static func getInstance() -> Suspension
}

class RoadSuspension: Suspension {
    var suspensionType = SuspensionOption.STANDARD
    //外界就不能直接創(chuàng)建
    private init(){}
    //只能通過這個方法返回實例
    class func getInstance() -> Suspension{
        return RoadSuspension()
    }
}

class OffRoadSuspension: Suspension {
    var suspensionType: SuspensionOption = SuspensionOption.SOFT
    
    private init(){}
    class func getInstance() -> Suspension{
        return OffRoadSuspension()
    }
}



class RaceSuspension:NSObject,NSCopying,Suspension{
   
    var suspensionType: SuspensionOption = SuspensionOption.SPORTS
    //因為它從NSObject繼承了一個空的init
    private override init(){}
    
    func copy(with zone: NSZone? = nil) -> Any {
        return RaceSuspension()
    }
    
    //單例模式
    private class var prototype:RaceSuspension{
    
        get{
            struct SingletonWrapper{
                static let singleton = RaceSuspension()
            }
            return SingletonWrapper.singleton
        }
    }
    //封裝copy
    class func getInstance() -> Suspension {
        return prototype.copy() as! Suspension
    }
}

因為Suspension不能通過實例化來生成對象了,只能通過getInstance生成了揖闸。修改Concrete.swift揍堕。

class CompactCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return StandardFloorplan()
    }
    override func creatSuspension() -> Suspension {
        return RoadSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return FrontWheelDrive()
    }
    //重寫父類方法,實現(xiàn)單例
    override class var sharedInstance:CarFatory? {
        get{
            struct SingletonWrapper{
                static let singleton = CompactCarFactory()
            }
            return SingletonWrapper.singleton
        }
    }
}

class SportsCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return ShortFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return RaceSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

class SUVCarFactory: CarFatory {
    override func createFloorplan() -> Floorplan {
        return LongFloorplan()
    }
    
    override func creatSuspension() -> Suspension {
        return OffRoadSuspension.getInstance()
    }
    
    override func creatDrivetrain() -> Drivetrain {
        return ALLWheelDrive()
    }
}

小結:

抽象工廠只包含選擇具體工廠的邏輯汤纸,而具體工廠只包含選擇實現(xiàn)類的邏輯衩茸,依次串起來。

demo:

SportsStoreDemo中的應用就需要自己去查看了贮泞,涉及到StockValueImplementations.swift楞慈、StockValueFactories.swiftViewController.swift都在這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末啃擦,一起剝皮案震驚了整個濱河市囊蓝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌令蛉,老刑警劉巖聚霜,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狡恬,死亡現(xiàn)場離奇詭異,居然都是意外死亡蝎宇,警方通過查閱死者的電腦和手機弟劲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姥芥,“玉大人兔乞,你說我怎么就攤上這事×固疲” “怎么了庸追?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熊榛。 經(jīng)常有香客問我锚国,道長腕巡,這世上最難降的妖魔是什么玄坦? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮绘沉,結果婚禮上煎楣,老公的妹妹穿的比我還像新娘。我一直安慰自己车伞,他們只是感情好择懂,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著另玖,像睡著了一般困曙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谦去,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天慷丽,我揣著相機與錄音,去河邊找鬼鳄哭。 笑死要糊,一個胖子當著我的面吹牛,可吹牛的內容都是我干的妆丘。 我是一名探鬼主播锄俄,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勺拣!你這毒婦竟也來了奶赠?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤药有,失蹤者是張志新(化名)和其女友劉穎毅戈,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡竹祷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年谈跛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塑陵。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡感憾,死狀恐怖,靈堂內的尸體忽然破棺而出令花,到底是詐尸還是另有隱情阻桅,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布兼都,位于F島的核電站嫂沉,受9級特大地震影響,放射性物質發(fā)生泄漏扮碧。R本人自食惡果不足惜趟章,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慎王。 院中可真熱鬧蚓土,春花似錦、人聲如沸赖淤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咱旱。三九已至确丢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吐限,已是汗流浹背鲜侥。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毯盈,地道東北人剃毒。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像搂赋,于是被迫代替她去往敵國和親赘阀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內容