将超类 return 的初始值设定项设为特定的子类?

Make initializer for superclass return a specific subclass?

我正在 Swift 中制作一个单位框架,它具有针对不同单位(例如质量和体积)的测量超class 和子classes。一个特性是允许框架正确地猜测它是用什么单元创建的并返回正确的class。示例代码:

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

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

库名为 Indus Valley 并于 GitHub

开源

让父 class 知道它的子class 继承(非常糟糕的反模式!)但这是可行的...

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

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

你可以通过使用面向设计模式来解决这个问题。只需要创建一个在内部使用所有其他 class 的接口 class 并创建对象和 return.

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

        return Measurement(unknownUnit)
    }
}

根据用例,这可能是一个合适的替代方案:

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
        }
    }
}