使用场景:继承,创建各种子类对象,一种继承关系
意义:
隐藏了选择子类、创建子类对象的过程,统一创建接口
原理描述:
这里强调一个管理类(通常用基类)、一个方法、返回不同子类对象。
当然如果有复杂的层级,只需要重复这个过程。
具体使用:
1、一个常用、错误示范。
创建一个FactoryMethod
的OS 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?{
let car = 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:Float
private 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?{
let car = 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:Float
private 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:Float
private 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.swift
、ProductDataStore
写在后面:
我写的关于设计模式内容,都是来自书《精通Swift设计模式》
,如果有兴趣可以直接买来看看,不用看我的"歪曲理解"。我只是一个搬运工,记录过程,记录一点浅显的理解。