Swift 中的自定义类集群
-
21-12-2019 - |
题
这是一个比较常见的设计模式:
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)
.