在Swift中定制类集群

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

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

它允许你从init调用中返回一个子类。

我试图找出使用Swift实现相同的事情的最佳方法。

我知道很可能有一个更好的方法来实现Swift同样的事情。 但是,我的课程将由我无法控制的现有Obj-C库进行初始化。 所以它需要以这种方式工作,并可以从Obj-C调用。

任何指针将非常感激。

我不相信这个模式可以在Swift中直接支持,因为初始化程序不像在Objective C中那样返回一个值 – 所以你没有机会返回一个替代的对象实例。

你可以使用types方法作为对象工厂 – 一个相当人为的例子是 –

 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 

创build类簇的“swifty”方法实际上是暴露协议而不是基类。

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

直到例如https://github.com/apple/swift-evolution/pull/247 (工厂初始值设定项)被接受和执行,我唯一能find的方法如下:

 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()不像Objective-C中的-init那样返回值,因此使用工厂方法似乎是最简单的select。

一个诀窍是把你的初始化器标记为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转换为包含实际基类的私有实例的代理类,并将所有内容转发给它。 没有消息转发,这适用于具有很less属性/方法的简单接口,但是对于更复杂的任何东西来说,这些接口是不便的

我认为实际上可以使用运行时函数在Swift中实现Cluster模式。 重点是在初始化时用新的子类replace你的新对象的类。 下面的代码工作正常,但我认为应该更多地关注子类的初始化。

 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)