这是一个比较常见的设计模式:

https://stackoverflow.com/a/17015041/743957

它允许您从您的返回子类 init 来电。

我正在尝试找出使用 Swift 实现相同目标的最佳方法。

我确实知道很可能有更好的方法可以用 Swift 实现同样的事情。然而,我的类将由我无法控制的现有 Obj-C 库初始化。所以它确实需要以这种方式工作并且可以从 Obj-C 调用。

任何指示将不胜感激。

有帮助吗?

解决方案

我不相信这种模式可以在SWIFT中直接支持,因为初始化者在Objective C中没有返回一个值 - 所以您没有机会返回备用对象实例。

您可以使用类型方法作为对象工厂 - 一个相当实例的示例是 -

class Vehicle
{
    var wheels: Int? {
      get {
        return nil
      }
    }

    class func vehicleFactory(wheels:Int) -> Vehicle
    {
        var retVal:Vehicle

        if (wheels == 4) {
            retVal=Car()
        }
        else if (wheels == 18) {
            retVal=Truck()
        }
        else {
            retVal=Vehicle()
        }

        return retVal
    }

}

class Car:Vehicle
{
    override var wheels: Int {
      get {
       return 4
      }
    }
}

class Truck:Vehicle
{
    override var wheels: Int {
      get {
          return 18
       }
     }
}
.

main.swift

let c=Vehicle.vehicleFactory(4)     // c is a Car

println(c.wheels)                   // outputs 4

let t=Vehicle.vehicleFactory(18)    // t is a truck

println(t.wheels)                   // outputs 18
.

其他提示

创建类集群的“Swifty”方式实际上是公开协议而不是基类。

显然,编译器禁止在协议或协议扩展上禁止静态函数。

直到例如 https://github.com/apple/sevift-evolution/pull/247 (Factory Initializers)被接受和实施,我唯一可以找到的方法如下:

import Foundation

protocol Building {
    func numberOfFloors() -> Int
}

func createBuilding(numberOfFloors numFloors: Int) -> Building? {
    switch numFloors {
    case 1...4:
        return SmallBuilding(numberOfFloors: numFloors)
    case 5...20:
        return BigBuilding(numberOfFloors: numFloors)
    case 21...200:
        return SkyScraper(numberOfFloors: numFloors)
    default:
        return nil
    }
}

private class BaseBuilding: Building {
    let numFloors: Int

    init(numberOfFloors:Int) {
        self.numFloors = numberOfFloors
    }

    func numberOfFloors() -> Int {
        return self.numFloors
    }
}

private class SmallBuilding: BaseBuilding {
}

private class BigBuilding: BaseBuilding {
}

private class SkyScraper: BaseBuilding {
}
.

// this sadly does not work as static functions are not allowed on protocols.
//let skyscraper = Building.create(numberOfFloors: 200)
//let bigBuilding = Building.create(numberOfFloors: 15)
//let smallBuilding = Building.create(numberOfFloors: 2)

// Workaround:
let skyscraper = createBuilding(numberOfFloors: 200)
let bigBuilding = createBuilding(numberOfFloors: 15)
let smallBuilding = createBuilding(numberOfFloors: 2)
.

自从 init() 不返回类似的值 -init 在 Objective C 中,使用工厂方法似乎是最简单的选择。

一个技巧是将你的初始化器标记为 private, , 像这样:

class Person : CustomStringConvertible {
    static func person(age: UInt) -> Person {
        if age < 18 {
            return ChildPerson(age)
        }
        else {
            return AdultPerson(age)
        }
    }

    let age: UInt
    var description: String { return "" }

    private init(_ age: UInt) {
        self.age = age
    }
}

extension Person {
    class ChildPerson : Person {
        let toyCount: UInt

        private override init(_ age: UInt) {
            self.toyCount = 5

            super.init(age)
        }

        override var description: String {
            return "\(self.dynamicType): I'm \(age). I have \(toyCount) toys!"
        }
    }

    class AdultPerson : Person {
        let beerCount: UInt

        private override init(_ age: UInt) {
            self.beerCount = 99

            super.init(age)
        }

        override var description: String {
            return "\(self.dynamicType): I'm \(age). I have \(beerCount) beers!"
        }
    }
}

这会导致以下行为:

Person.person(10) // "ChildPerson: I'm 10. I have 5 toys!"
Person.person(35) // "AdultPerson: I'm 35. I have 99 beers!"
Person(35) // 'Person' cannot be constructed because it has no accessible initializers
Person.ChildPerson(35) // 'Person.ChildPerson' cannot be constructed because it has no accessible initializers

它不像 Objective C 那样好,因为 private 意味着所有子类都需要在同一个源文件中实现,并且存在细微的语法差异 Person.person(x) (或者 Person.create(x) 或其他)而不是简单地 Person(x), ,但实际上来说,它的作用是一样的。

能够从字面上实例化为 Person(x), ,你可以转 Person 到一个代理类中,该类包含实际基类的私有实例并将所有内容转发给它。如果没有消息转发,这适用于具有很少属性/方法的简单接口,但对于更复杂的东西来说它会变得笨拙:P

我认为实际上可以使用运行时函数SWIFT实现群集模式。主要点是在初始化时用子类替换新对象的类。下面的代码正常工作,虽然我认为更多关注要支付给子类初始化。

class MyClass
{
    var name: String?

    convenience init(type: Int)
    {
        self.init()

        var subclass: AnyClass?
        if type == 1
        {
            subclass = MySubclass1.self
        }
        else if type == 2
        {
            subclass = MySubclass2.self
        }

        object_setClass(self, subclass)
        self.customInit()
    }

    func customInit()
    {
        // to be overridden
    }
}

class MySubclass1 : MyClass
{
    override func customInit()
    {
        self.name = "instance of MySubclass1"
    }
}

class MySubclass2 : MyClass
{
    override func customInit()
    {
        self.name = "instance of MySubclass2"
    }
}

let myObject1 = MyClass(type: 1)
let myObject2 = MyClass(type: 2)
println(myObject1.name)
println(myObject2.name)
.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top