创建型模式-工厂方法

使用场景:继承,创建各种子类对象,一种继承关系
意义: 隐藏了选择子类、创建子类对象的过程,统一创建接口
原理描述: 这里强调一个管理类(通常用基类)、一个方法、返回不同子类对象。
当然如果有复杂的层级,只需要重复这个过程。
创建型模式-工厂方法
文章图片
工厂模式原理图.png 具体使用: 1、一个常用、错误示范。 创建一个FactoryMethodOS X命令行工程,创建一个RentalCar.swift文件
//协议 protocol RentalCar{ var name:String{get} var passengers:Int{get} var pricePerDay:Float{get} }//协议继承 class Compact: RentalCar { var name = "VM Golf" var passengers = 3 var pricePerDay: Float = 20 }class Sports:RentalCar{var name = "Porsche Boxter" var passengers: Int = 1 var pricePerDay: Float = 100 }class SUV: RentalCar { var name = "Cadillac Escalade" var passengers: Int = 8 var pricePerDay: Float = 75 }

再创建一个CarSelector.swift,一个调用类
class CarSelector{class func selectCar(passengers:Int) -> String?{ var car:RentalCar? switch passengers { case 0...1: car = Sports() case 2...3: car = Compact() case 4...8: car = SUV() default: car = nil } return car?.name } }

最后在main.swift中使用
let passengers = [1,3,5]for p in passengers{print("\(p) pasengers:\(CarSelector.selectCar(passengers: p)!)") }

使用的时候好像没有什么问题,新增一个类型的时候,看看需要改变哪些地方。
RentalCar.swift新增一个类型Minivan
... class SUV: RentalCar { var name = "Cadillac Escalade" var passengers: Int = 8 var pricePerDay: Float = 75 } //新增类型 class Mininvan: RentalCar{var name = "Chevrolet Evpress" var passengers = 14 var pricePerDay: Float = 40 }

新增类型后,CarSelector.swift也需要更改:
class CarSelector{class func selectCar(passengers:Int) -> String?{ var car:RentalCar? switch passengers { case 0...1: car = Sports() case 2...3: car = Compact() case 4...8: car = SUV() //新增逻辑 case 9...14: car = Mininvan() default: car = nil } return car?.name } }

1.1、问题:CarSelector需要知道所有子类类型,一旦新增子类,就需要更新逻辑。 新增一个调用类PriceCalculator.swift,还会有哪些问题?
class PriceCalculator{class func calculatorPrice(passengers:Int,days:Int) -> Float?{ var car:RentalCar? switch passengers { case 0...1: car = Sports() case 2...3: car = Compact() case 4...8: car = SUV() //新增逻辑 case 9...14: car = Mininvan() default: car = nil } return car == nil ? nil : car!. pricePerDay* Float(days) } }

1.2、问题:新增调用类,会重写一遍选择逻辑。思考一下,这段选择逻辑是可以隐藏起来的,调用类根本不需要关心这些,它只需要一个子类对象就可以了。 2、解决办法:封装选择逻辑,提供调用类一个获取对象的方法即可。 2.1、全局方法: 在RentalCar.swift中创建一个全局方法封装选择逻辑:
func creatRentalCar(passengers:Int) -> RentalCar?{var car:RentalCar?switch passengers { case 0...1: car = Sports() case 2...3: car = Compact() case 4...8: car = SUV() case 9...14: car = Mininvan() default: car = nil } return car }protocol RentalCar{var name:String{get} var passengers:Int{get} var pricePerDay:Float{get} } ...

CarSelector.swift使用起来就是这样的
class func selectCar(passengers:Int) -> String?{return creatRentalCar(passengers: passengers)?.name }

PriceCalculator.swift
class func calculatePrice(passengers:Int,days:Int) -> Float?{letcar = creatRentalCar(passengers: passengers) return car == nil ? nil : car!.pricePerDay * Float(days) }

选择逻辑完全隐藏起来,新增类、删除类,调用类根本不用变动。
2.2、使用基类 改造RentalCar.swift,使用基类替换协议,适当减少子类,为后续改变做准备。
class RentalCar{ private var nameBV :String private var passengersBV:Int private var pricePerDayBV:Floatprivate init(name:String,passengers:Int,price:Float) { self.nameBV = name self.passengersBV = passengers self.pricePerDayBV = price }final var name:String{ get{ return nameBV } }final var passengers:Int{ get { return passengersBV} }final var pricePerDay:Float{ get { return pricePerDayBV } } class func creatRentalCar(passengers:Int) -> RentalCar?{var carI:RentalCar?switch passengers { case 0...3: car = Compact() case 4...8: car = SUV() default: car = nil } return car } class Compact:RentalCar{fileprivate init(){ self.init(name: "VM Golf", passengers: 3, price: 20) } }class SUV:RentalCar{fileprivate init(){ super.init(name: "Cadillac Escalade", passengers: 8, price: 75) } } }

使用基类时,为了达到协议的效果,使用final关键字修饰属性,避免属性被修改。还定义了一个私有构造方法,需要传递参数。
CarSelector.swift使用是这样的:
class func selectCar(passengers:Int) -> String?{return RentalCar.creatRentalCar(passengers: passengers)?.name }

PriceCalculator.swift
class func calculatePrice(passengers:Int,days:Int) -> Float?{letcar = RentalCar.creatRentalCar(passengers: passengers) return car == nil ? nil : car!.pricePerDay * Float(days) }

以下可选,对理解工厂模式影响不大,只是应用得更复杂,如果只是简单运用,到这里就可以了。 如果你能看懂我画的原理图,那么似乎还有一种稍微复杂的场景,子类里面还有子类实现。
3、子类里还有子类,可以把选择逻辑放到更深,如原理图。 RentalCar.swift新增一个继承Compact的子类SmallCompact
class RentalCar{ private var nameBV :String private var passengersBV:Int private var pricePerDayBV:Floatprivate init(name:String,passengers:Int,price:Float) { self.nameBV = name self.passengersBV = passengers self.pricePerDayBV = price }final var name:String{ get{ return nameBV } }final var passengers:Int{ get { return passengersBV} }final var pricePerDay:Float{ get { return pricePerDayBV } } class func creatRentalCar(passengers:Int) -> RentalCar?{//元类型,RentalCar的任意类型,包括本身类型、还有子类类型 var carImpl:RentalCar.Type?switch passengers { case 0...3: carImpl = Compact.self case 4...8: carImpl = SUV.self default: carImpl = nil } return carImpl?.creatRentalCar(passengers: passengers) }class Compact:RentalCar{fileprivate convenience init(){ self.init(name: "VM Golf", passengers: 3, price: 20) }fileprivate override init(name:String,passengers:Int,price:Float){ super.init(name: name, passengers: passengers, price: price) } //override重写父类方法,子类的选择逻辑 override class func creatRentalCar(passengers:Int) -> RentalCar?{ if passengers < 2 { return Compact() }else{ return SmallCompact() } }class SmallCompact: Compact { fileprivate init(){ super.init(name: "Ford Fiesta", passengers: 3, price: 15) } }class SUV:RentalCar{fileprivate init(){ super.init(name: "Cadillac Escalade", passengers: 8, price: 75) }override class func creatRentalCar(passengers:Int) -> RentalCar?{ return SUV() } } }

  • 里面使用到.Type,个人理解就是RentalCar的任意类型,包括本身类型、还有子类类型。.self就是具体的类型,这里就是指每个子类。如果你要了解详情,可以看看这个、还有王巍 (@ONEVCAT)的TIP。
3.1、还可以加入单例模式 其实用不用单例都无关紧要,因为调用类根本不关心细节。
class RentalCar{ private var nameBV :String private var passengersBV:Int private var pricePerDayBV:Floatprivate init(name:String,passengers:Int,price:Float) { self.nameBV = name self.passengersBV = passengers self.pricePerDayBV = price }final var name:String{ get{ return nameBV } }final var passengers:Int{ get { return passengersBV} }final var pricePerDay:Float{ get { return pricePerDayBV } } class func creatRentalCar(passengers:Int) -> RentalCar?{//元类型,RentalCar的任意类型,包括本身类型、还有子类类型 var carImpl:RentalCar.Type?switch passengers { case 0...3: carImpl = Compact.self case 4...8: carImpl = SUV.self default: carImpl = nil } return carImpl?.creatRentalCar(passengers: passengers) }class Compact:RentalCar{fileprivate convenience init(){ self.init(name: "VM Golf", passengers: 3, price: 20) }fileprivate override init(name:String,passengers:Int,price:Float){ super.init(name: name, passengers: passengers, price: price) } //override重写父类方法 override class func creatRentalCar(passengers:Int) -> RentalCar?{ if passengers < 2 { return shareInstance }else{ return SmallCompact.shareInstance } }//单例 class var shareInstance:RentalCar{get{ struct SingletonWrapper{ static let singleton = Compact() } return SingletonWrapper.singleton } } }class SmallCompact: Compact { fileprivate init(){ super.init(name: "Ford Fiesta", passengers: 3, price: 15) }override class var shareInstance:Compact{ get{ struct SingletonWrapper{ static let singleton = SmallCompact() } return SingletonWrapper.singleton }} }class SUV:RentalCar{fileprivate init(){ super.init(name: "Cadillac Escalade", passengers: 8, price: 75) }override class func creatRentalCar(passengers:Int) -> RentalCar?{ return SUV() } } }

总结: 提供一个方法,封装好选择子类对象的逻辑,避免暴露细节。
  • Demo在这里,05工厂模式SportsStoreDemo里面涉及的类Product.swiftProductDataStore
写在后面: 【创建型模式-工厂方法】我写的关于设计模式内容,都是来自书《精通Swift设计模式》,如果有兴趣可以直接买来看看,不用看我的"歪曲理解"。我只是一个搬运工,记录过程,记录一点浅显的理解。

    推荐阅读