Pergunta

Isso é relativamente comum padrão de design:

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

Ele permite que você retornar uma subclasse a partir do seu init chamadas.

Eu estou tentando descobrir o melhor método para atingir a mesma coisa usando Swift.

Eu sei que é muito provável que haja uma melhor método para atingir a mesma coisa com o Swift.No entanto, minha aula vai ser inicializado por um existente Obj-biblioteca C que eu não tenho controle.Então, ela não precisa trabalhar desta forma, e de ser chamada a partir de Obj-C.

Qualquer ponteiros seria muito apreciado.

Foi útil?

Solução

Eu não acredito que este padrão pode ser diretamente suportadas em Swift, porque inicializadores não retornam um valor, como eles fazem em objective-C - assim você não conseguir uma oportunidade para retornar uma alternativa instância do objeto.

Você pode usar um tipo de método como um objeto de fábrica bastante artificial exemplo é -

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

Outras dicas

O "swifty" caminho de criar classe de clusters seria, na verdade, para expor um protocolo, em vez de uma classe base.

Aparentemente, o compilador proíbe funções estáticas em protocolos ou extensões do protocolo.

Até e.g. https://github.com/apple/swift-evolution/pull/247 (fábrica de inicializadores) é aceito e implementado, a única maneira que eu poderia encontrar para fazer isso é o seguinte:

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() não devolução de valores como -init não em objective-C, usando um método de fábrica parece ser a opção mais fácil.

Um truque é para marcar o seu 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!"
        }
    }
}

Isso resulta no seguinte 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

Ele não é tão bom como objective C, desde private significa que todas as subclasses devem ser aplicadas no mesmo arquivo de origem, e não há a menor diferença de sintaxe Person.person(x) (ou Person.create(x) ou seja o que for) em vez de simplesmente Person(x), mas , na prática, ele funciona da mesma.

Para ser capaz de instanciar literalmente como Person(x), você pode transformá Person em uma classe proxy que contém uma instância particular do real, da base de dados de classe e encaminha tudo para ele.Sem o encaminhamento de mensagens, isso funciona para interfaces simples com algumas propriedades/métodos, mas fica complicado para qualquer coisa mais complexa :P

Eu acho que, na verdade, o Cluster padrão pode ser implementado em Swift usando funções de tempo de execução.O ponto principal é substituir a classe de seu novo objeto de uma subclasse, durante a inicialização.O código abaixo funciona bem, embora eu acho que mais atenção deve ser dada à subclasse' inicialização.

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top