Swiftのカスタムクラスクラスタ
-
21-12-2019 - |
解決
このパターンがSwiftで直接サポートできるとは思わない、initialisersが目的の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/swift-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
のような値を返しませんCoveTive Cは、工場出荷時の方法を使用して、最も簡単なオプションのようです。
1つのトリックは、このように初期化業者を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
.
private
はすべてのサブクラスを同じソースファイルに実装する必要があることを意味し、単純な構文の違いの代わりに、Person.person(x)
の代わりにマイナーな構文差の違い(またはPerson.create(x)
または任意のもの)を意味しますが、実質的に言っていることがわかります。 、それは同じように機能します。
文字通りPerson(x)
としてインスタンス化できるようにするには、実際の基本クラスのプライベートインスタンスを含むプロキシクラスにPerson(x)
を入力して、すべてをそれに転送することができます。メッセージ転送なしでは、プロパティ/メソッドが少ない単純なインターフェイスでは機能しますが、それ以上の複雑なものには扱いにくいです.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)
.