Domanda

Questo è un modello di design relativamente comune:

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

consente di restituire una sottoclasse dalle chiamate init.

Sto cercando di capire il metodo migliore per raggiungere la stessa cosa usando Swift.

Lo so che è molto probabile che vi sia un metodo migliore per raggiungere la stessa cosa con Swift.Tuttavia, la mia classe sarà inizializzata da una biblioteca OBJ-C esistente che non ho il controllo.Quindi è necessario funzionare in questo modo ed essere chiamabile da obj-c.

Qualsiasi puntatori sarebbe molto apprezzato.

È stato utile?

Soluzione

Non credo che questo modello possa essere supportato direttamente in Swift, perché le iniziazioni non restituiscono un valore come fanno in Objective C - quindi non si ottiene un'opportunità per restituire un'istanza dell'oggetto alternativa.

È possibile utilizzare un metodo di tipo come fabbrica di oggetti - un esempio abbastanza esclusivo è -

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
.

Altri suggerimenti

Il modo "Swifty" di creare cluster di classe sarebbe effettivamente esporre un protocollo anziché una classe base.

Apparentemente il compilatore proibisce le funzioni statiche sui protocolli o le estensioni del protocollo.

Fino ad es. https://github.com/apple/swift-evolution/pull/247 (Inizializzatori di fabbrica) sono accettate e implementate, l'unico modo in cui ho potuto trovare questo è il seguente:

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)
.

Poiché init() non restituisce valori come -init in Objective C, utilizzando un metodo di fabbrica sembra un'opzione più semplice.

Un trucco è segnare le tue inizializzatori come private, come questa:

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!"
        }
    }
}
.

Questo risulta nel seguente comportamento:

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
.

Non è bello come Obiettivo C, poiché private significa che tutte le sottoclassi devono essere implementate nello stesso file sorgente, e c'è che la differenza minori della sintassi generacodicatagcode (o Person.person(x) o qualsiasi altra) invece di semplicemente Person.create(x), ma praticamente parlando , Funziona lo stesso.

Per essere in grado di istanziare letteralmente come Person(x), è possibile trasformare Person(x) in una classe proxy che contiene un'istanza privata della classe base effettiva e inoltra tutto ad esso. Senza inoltro del messaggio, funziona per interfacce semplici con poche proprietà / metodi ma diventa ingombrante per qualcosa di più complesso: P

Penso che in realtà il modello del cluster possa essere implementato in Swift utilizzando le funzioni di runtime.Il punto principale è sostituire la classe del tuo nuovo oggetto con una sottoclasse durante l'inizializzazione.Il codice qui sotto funziona bene anche se penso che la maggiore attenzione dovrebbe essere pagata all'inizializzazione della sottoclasse.

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)
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top