问题描述
这是一个相对常见的设计模式:
https://stackoverflow.com/a/a/17015041/743957
它允许您从init呼叫返回子类.
我试图找出使用Swift实现同一事物的最佳方法.
我确实知道,很有可能有一种更好的方法可以通过Swift实现同样的事情.但是,我的班级将由我无法控制的现有OBJ-C库初始化.因此,它确实需要以这种方式工作,并可以从OBJ-C中呼唤
任何指针都将不胜感激.
推荐答案
我不相信这种模式可以在Swift中直接支持,因为初始评估器不会像目标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/apple/swift-evoltion/swift-evolution/pull/pull/247 (工厂初始化器)被接受和实施,我能找到的唯一方法是:
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在目标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
它不像目标C那样好,因为private表示所有子类都需要在同一源文件中实现,并且次要语法差异Person.person(x)(或Person.create(x)或erthing)而不是简单地Person(x),但实际上,它起作用.
为了能够以Person(x)的字面实例化,您可以将Person变成一个代理类,该类包含实际基类的私人实例,并将所有内容转发到其中.没有消息转发,这适用于具有很少属性/方法的简单接口,但对于更复杂的任何内容而言,它变得笨拙:P
问题描述
This is a relatively common design pattern:
https://stackoverflow.com/a/17015041/743957
It allows you to return a subclass from your init calls.
I'm trying to figure out the best method of achieving the same thing using Swift.
I do know that it is very likely that there is a better method of achieving the same thing with Swift. However, my class is going to be initialized by an existing Obj-C library which I don't have control over. So it does need to work this way and be callable from Obj-C.
Any pointers would be very much appreciated.
推荐答案
I don't believe that this pattern can be directly supported in Swift, because initialisers do not return a value as they do in Objective C - so you do not get an opportunity to return an alternate object instance.
You can use a type method as an object factory - a fairly contrived example is -
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
其他推荐答案
The "swifty" way of creating class clusters would actually be to expose a protocol instead of a base class.
Apparently the compiler forbids static functions on protocols or protocol extensions.
Until e.g. https://github.com/apple/swift-evolution/pull/247 (factory initializers) is accepted and implemented, the only way I could find to do this is the following:
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)
其他推荐答案
Since init() doesn't return values like -init does in Objective C, using a factory method seems like the easiest option.
One trick is to mark your initializers as private, like this:
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!" } } }
This results in the following behavior:
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
It's not quite as nice as Objective C, since private means all the subclasses need to be implemented in the same source file, and there's that the minor syntax difference Person.person(x) (or Person.create(x) or whatever) instead of simply Person(x), but practically speaking, it works the same.
To be able to instantiate literally as Person(x), you could turn Person into a proxy class which contains a private instance of the actual base class and forwards everything to it. Without message forwarding, this works for simple interfaces with few properties/methods but it gets unwieldy for anything more complex :P