Question

Il s'agit d'un modèle de conception relativement courant :

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

Il vous permet de renvoyer une sous-classe de votre init appels.

J'essaie de trouver la meilleure méthode pour réaliser la même chose en utilisant Swift.

Je sais qu'il est très probable qu'il existe une meilleure méthode pour réaliser la même chose avec Swift.Cependant, ma classe va être initialisée par une bibliothèque Obj-C existante sur laquelle je n'ai aucun contrôle.Il doit donc fonctionner de cette façon et pouvoir être appelé depuis Obj-C.

Tous les indicateurs seraient très appréciés.

Était-ce utile?

La solution

Je ne crois pas que ce modèle puisse être directement pris en charge dans Swift, car les initialiseurs ne renvoient pas de valeur comme ils le font dans Objective C - vous n'avez donc pas la possibilité de renvoyer une autre instance d'objet.

Vous pouvez utiliser une méthode de type comme fabrique d'objets - un exemple assez artificiel est -

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
       }
     }
}

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

Autres conseils

La manière « rapide » de créer des clusters de classes serait en fait d’exposer un protocole au lieu d’une classe de base.

Apparemment, le compilateur interdit les fonctions statiques sur les protocoles ou les extensions de protocole.

Jusqu'à par ex. https://github.com/apple/swift-evolution/pull/247 (initialiseurs d'usine) est accepté et implémenté, la seule façon que j'ai pu trouver pour ce faire est la suivante :

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)

Depuis init() ne renvoie pas de valeurs comme -init fait en Objective C, utiliser une méthode d'usine semble être l'option la plus simple.

Une astuce consiste à marquer vos initialiseurs comme private, comme ça:

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

Cela se traduit par le comportement suivant :

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

Ce n'est pas aussi sympa qu'Objective C, puisque private signifie que toutes les sous-classes doivent être implémentées dans le même fichier source, et il y a une différence de syntaxe mineure Person.person(x) (ou Person.create(x) ou autre) au lieu de simplement Person(x), mais en pratique, cela fonctionne de la même manière.

Pour pouvoir instancier littéralement comme Person(x), tu pourrais tourner Person dans une classe proxy qui contient une instance privée de la classe de base réelle et lui transmet tout.Sans transfert de message, cela fonctionne pour des interfaces simples avec peu de propriétés/méthodes mais cela devient compliqué pour quelque chose de plus complexe :P

Je pense qu'en fait, le modèle Cluster peut être implémenté dans Swift à l'aide de fonctions d'exécution.Le point principal est de remplacer la classe de votre nouvel objet par une sous-classe lors de l'initialisation.Le code ci-dessous fonctionne bien, même si je pense qu'une plus grande attention devrait être accordée à l'initialisation de la sous-classe.

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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top