Pregunta

Este es un fenómeno relativamente común patrón de diseño:

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

Permite devolver una subclase de su init las llamadas.

Estoy tratando de averiguar cuál es el mejor método para lograr lo mismo mediante el uso de Swift.

Sé que es muy probable que hay un mejor método para lograr la misma cosa con Swift.Sin embargo, mi clase va a ser inicializado por un Obj-C de la biblioteca que no tengo control.Así que se hace necesario trabajar de esta manera y se puede llamar desde Obj-C.

Los punteros sería muy apreciado.

¿Fue útil?

Solución

No creo que este patrón pueda ser apoyado directamente en SWIFT, porque los inicializadores no devuelven un valor como lo hacen en el objetivo C, por lo que no tiene la oportunidad de devolver una instancia de objetos alternativos.

Puede usar un método de tipo como una fábrica de objetos: un ejemplo de manera bastante reconocida es -

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

Otros consejos

La forma "swifty" de crear clústeres de clase en realidad sería exponer un protocolo en lugar de una clase base.

Aparentemente, el compilador prohíbe las funciones estáticas en los protocolos o las extensiones de protocolo.

hasta que ej. https://github.com/apple/swift-evolution/pull/247 (Los inicializadores de fábrica) se aceptan e implementan, la única forma en que podría encontrarlo es lo siguiente:

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)

Desde init() no devuelve valores como -init en Objective C, utilizando un método de fábrica parece la opción más fácil.

Un truco es marcar su inicializadores como private, como este:

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

Esto se traduce en el comportamiento siguiente:

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

No es tan agradable como Objective C, ya que private significa que todas las subclases deben ser aplicadas en el mismo archivo de origen, y no de que el menor sintaxis diferencia Person.person(x) (o Person.create(x) o lo que sea) en lugar de simplemente Person(x), pero en la práctica, funciona de la misma.

Para ser capaz de crear una instancia literalmente como Person(x), se puede dar la Person en una clase de proxy que contiene una instancia privada de la base real de la clase y remite todo a ella.Sin reenvío de mensajes, esto funciona para las interfaces simples con pocas propiedades/métodos pero se hace difícil de manejar para cualquier cosa más compleja :P

Creo que en realidad el patrón de clúster se puede implementar en SWIFT usando funciones de tiempo de ejecución.El punto principal es reemplazar la clase de su nuevo objeto con una subclase al inicializar.El código a continuación funciona bien, aunque creo que se debe prestar más atención a la inicialización de la subclase.

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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top