美文网首页
创建型模式-工厂方法

创建型模式-工厂方法

作者: 关灯侠 | 来源:发表于2017-03-27 02:05 被阅读11次

    使用场景:继承,创建各种子类对象,一种继承关系

    意义:

    隐藏了选择子类、创建子类对象的过程,统一创建接口

    原理描述:

    这里强调一个管理类(通常用基类)、一个方法、返回不同子类对象。
    当然如果有复杂的层级,只需要重复这个过程。

    工厂模式原理图.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?{
            
            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.swiftProductDataStore

    写在后面:

    我写的关于设计模式内容,都是来自书《精通Swift设计模式》,如果有兴趣可以直接买来看看,不用看我的"歪曲理解"。我只是一个搬运工,记录过程,记录一点浅显的理解。

    相关文章

      网友评论

          本文标题:创建型模式-工厂方法

          本文链接:https://www.haomeiwen.com/subject/tyhwnttx.html