【问题标题】:Make initializer for superclass return a specific subclass? [duplicate]使超类的初始化程序返回特定的子类? [复制]
【发布时间】:2015-05-25 11:29:20
【问题描述】:

我正在用 Swift 制作一个单位框架,它具有不同单位的测量超类和子类,例如质量和体积。一个功能是允许框架正确猜测它是用什么单元创建的并返回正确的类。示例代码:

class Measurement {
   var unitString : String
   init(unknownUnit: String) {
       // checks if the unit is either Volume or Mass, and returns an instance of that class
   }
}

class Volume : Measurement {
    init(unitString: String) {

    }
}

class Mass : Measurement {
    init(unitString: String) {

    }
}

let mass = Mass("kg")                   // class: Mass
let volume = Volume("ml")               // class: Volume
let shouldBeVolume = Measurement("ml")  // class: Volume
let shouldBeMass = Measurement("kg")    // class: Mass

是否可以让继承的类在初始化时创建特定子类的对象?

库名为Indus Valley,在 GitHub 上开源

【问题讨论】:

  • 这绝对是 Swift init 不返回值的一个恼人限制。

标签: swift subclass


【解决方案1】:

让父类知道它的子类(非常糟糕的反模式!),但是这会起作用......

class Measurement {
    var unitString : String

    class func factory(unknownUnit: String) -> Measurement {
        if unknownUnit == "kg" {
            return Mass(myUnit: unknownUnit)
        } else { // Random default, or make func return Measurement? to trap
            return Volume(myUnit: unknownUnit)
        }
    }

    init(myUnit: String) {
        // checks if the unit is either Volume or Mass, and returns an instance of that class
        self.unitString = myUnit
    }
}

class Volume : Measurement {
}

class Mass : Measurement {
}

let mass = Mass(myUnit: "kg")                   // class: Mass
let volume = Volume(myUnit: "ml")               // class: Volume
let shouldntBeVolume = Measurement(myUnit: "ml")  // class: Measurement
let shouldntBeMass = Measurement(myUnit: "kg")    // class: Measurement
let isVolume = Measurement.factory("ml")  // class: Volume
let shouldBeMass = Measurement.factory("kg")    // class: Mass

【讨论】:

    【解决方案2】:

    如果您在超类中创建子类对象,那么您的应用程序将崩溃,因为对 init 方法的调用将是递归的。要进行测试,您可以只创建类的层次结构并尝试在超类中创建子类对象。

    您可以通过使用面向设计模式来解决此问题。只需要创建一个在内部使用所有其他类的接口类并创建对象并返回。

    class UnitConverter {
        class func measurement(unknownUnit: String) -> Measurement {
            if unknownUnit == "kg" {
                return Mass(unknownUnit)
            } else if unknownUnit == "ml" {
                return Volume(unknownUnit)
            }
    
            return Measurement(unknownUnit)
        }
    }
    

    【讨论】:

    • 只有当子类调用父类的 same 初始化器时才会这样。但是,如果子类调用超类的特殊(例如私有)初始化程序,它不会无限递归。这就是它在 Obj-C 中的工作方式。
    【解决方案3】:

    根据用例,也许这可能是一个合适的选择:

    enum Measurement {
        case Mass
        case Volume
    
        static func fromUnit(unit:String) -> Measurement? {
            switch unit {
                case "mg", "g", "kg": return Mass
                case "ml", "dl", "l": return Volume
    
                default: return nil
            }
        }
    }
    

    【讨论】:

    • 是的,这就是我目前的解决方案,想知道是否有人有任何初始化程序:)
    • 如果要为此使用初始化语法,可以将静态方法更改为 init (init?(unit:String)),然后使用 self = .Massself = .Volume 创建 @987654325 的实例@
    猜你喜欢
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-10
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多