Classe personalizada clusters Swift
-
21-12-2019 - |
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.
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)