2016-01-12 4 views
5

Ich möchte eine Factory-Funktion erstellen, die einen Klassentyp akzeptiert und einen Konstruktor zurückgibt, damit ich sie verwenden kann Dieser Konstruktor erstellt später eine Instanz dieser Klasse.Swift: Erstellen einer Factory-Funktion, die einen Klassentyp als Parameter akzeptiert und einen Konstruktor dieser Klasse ausgibt

Stellen Sie sich vor, ich habe zwei Klassen, Apple und Orange, die beide Unterklassen von Fruit sind. Sie müssen mit einem unknownNumber initialisiert werden, von dem ich erst später weiß.

class Apple: Fruit { 
    init(unknownNumber: Int) { 
     ... 
    } 
} 

class Orange: Fruit { 
    init(unknownNumber: Int) { 
     ... 
    } 
} 

Ich möchte eine Fabrik-Funktion erstellen, die in einer Klasse-Typ nimmt, so dass ich später diese Funktion aufrufen können und die spezifische Unterklasse von Obst, mit dem unknownNumber initialisieren.

//concept: 
func makeFruit(typeOfFruit) -> (Int) -> Fruit { 
    return { (unknownNumber: Int) -> Fruit in 
     return typeOfFruit(unknownNumber) 
    } 
} 

Um eine orangeFactory, dann zu erstellen, was ich tun kann:

let orangeFactory = makeFruit(Orange)  

// then at a later time when I have the unknown number 
let orangeInstance = orangeFactory(unknownNumber) 

Ich bin mir der Möglichkeit, einfach den unknownNumber einen faulen Variable zu machen, aber in meinem speziellen Fall die unknownNumber ist nicht nur eine Nummer und es beinhaltet andere Prozesse, also möchte ich nur das Objekt erstellen, wenn ich alles zur Verfügung habe, um die Struktur einfach zu halten.

Ist so etwas in Swift möglich? Ich recherchiere seit einiger Zeit online und konnte keine direkten Antworten finden. Jede Hilfe würde sehr geschätzt werden!

Antwort

4

Lassen Sie uns rückwärts arbeiten. In Ihrer makeFruit Funktion, müssen Sie den typeOfFruit Parameter als Metatyp Ihrer Fruit geordneten Klasse erklären und verweisen ausdrücklich auf die initializer:

func makeFruit(typeOfFruit: Fruit.Type) -> (Int) -> Fruit { 
    return { (unknownNumber: Int) -> Fruit in 
     return typeOfFruit.init(unknownNumber: unknownNumber) 
    } 
} 

Sie nur required initializers auf einem Metatyp zugreifen können, so dass init Bedürfnisse sein als solcher gekennzeichnet ist:

class Fruit { 
    required init(unknownNumber: Int) { 
     // ... 
    } 
} 

Der Rest einfach funktionieren sollte:

let orangeMaker = makeFruit(Orange.self) 
let tenOranges = orangeMaker(10) 
+0

Dies ist eine ausgezeichnete Antwort. Die Methode scheint so einfach zu sein, erfordert aber auch die Kenntnis vieler kleiner Eigenheiten von Swifts Objekterzeugungsmodell. Ich probiere das jetzt aus und werde Sie bald wissen lassen, ob es funktioniert! – gokeji

0

Sie können Fruit als Protokoll erklären, dass die erforderliche init Methode und nutzen die Generika-Unterstützung in Switf aussetzt:

protocol Fruit { 
    init(unknownNumber: Int) 
} 

class Apple: Fruit { 
    required init(unknownNumber: Int) { 

    } 
} 

class Orange: Fruit { 
    required init(unknownNumber: Int) { 

    } 
} 

func makeFruit<T: Fruit>(cls: T.Type) -> Int -> T { 
    return { T(unknownNumber: $0) } 
} 

makeFruit(Apple.self)(10) // returns an Apple 
makeFruit(Orange.self)(15) // returns an Orange 

Dies bekommt auch Sie geben Sicherheit als das Ergebnis der makeFruit Funktion ist die gleiche wie der Typ, der durch den Parameter cls angegeben wird.

Beachten Sie, dass dies keine Werksfunktion ist, sondern lediglich eine Weiterleitungsfunktion. Aber man kann sogar noch weiter gehen und anpassen makeFruit für einige der Früchte, und das ist, was er eine Fabrik-Funktion macht:

class Apple: Fruit { 
    required init(unknownNumber: Int) { 

    } 

    init(color: String, unknownNumber: Int) { 

    } 
} 

func makeFruit<T: Fruit>(cls: T.Type) -> Int -> T { 
    return { T(unknownNumber: $0) } 
} 

func makeFruit(cls: Apple.Type) -> Int -> Apple { 
    return { Apple(color: "red", unknownNumber: $0) } 
} 

makeFruit(Orange.self)(15) // an Orange 
makeFruit(Apple.self)(10) // a red Apple 

Unter der Annahme, dass die Apple Klasse einen Initialisierer hat, die eine Farbe annehmen, können wir makeFruit außer Kraft setzen für diese spezifische Klasse und übergeben Sie eine Standardfarbe (oder eine berechnete Farbe, abhängig von den Werksspezifikationen). Dies ohne die Sicherheit zu verlieren, die mit Swift auf Sie zukommt.

+1

liebe down-Wähler! Bitte lassen Sie uns wissen, was Sie in dieser Antwort falsch sehen. Alle Antworten (Nates, Cristiks ad Alains) verwenden im Grunde den gleichen Ansatz und alle unterscheiden sich nur in kleinen Details. Warum wird Criktiks Antwort abgelehnt? BITTE! Wenn jemand eine Antwort nicht mag, lassen Sie uns den Grund wissen! – user3441734

+0

Hallo Cristik, vielen Dank für die nachdenkliche Antwort. Ich hatte noch nie eine Herangehensweise mit Generika in Erwägung gezogen, und Ihre Antwort hat mir sicherlich einiges beigebracht. Ich habe die Antwort von Nate akzeptiert, da sie genau das liefert, wonach ich gefragt habe, in einer ziemlich geradlinigen Art und Weise, aber ich schätze Ihre Eingabe sehr. Entschuldigung, dass jemand diese Antwort abgelehnt hat; Ich habe es aufgewertet. – gokeji

+1

Mach dir keine Sorgen Jungs über die Down-Stimmen, ich nicht :) – Cristik

1

Wenn Sie die Klasse selbst als Kennung für einen Fabrikeintrag verwenden möchten, benötigen Sie keine Fabrik.Das Factory-Muster erzeugt eine Indirektion zwischen einem beliebigen Bezeichner und einer entsprechenden Klasse von Objekten.

Eine einfache Möglichkeit, dies in Swift zu tun ist, um ein Wörterbuch zu verwenden:

var fruitFactory:[String:Fruit.Type] = [:] 

fruitFactory["Apple"] = Apple.self 
fruitFactory["Orange"] = Orange.self 
fruitFactory["Banana"] = Fruit.self 
fruitFactory["Grape"] = Fruit.self 

let tenOranges = fruitFactory["Orange"]!.init(unknownNumber:10) 

beachten Sie, dass Ihre initializer in der Fruit Klasse markiert werden muss, wie dies erforderlich ist, um zu arbeiten.

+0

Vielen Dank für die Antwort. Sobald ich von der Möglichkeit erfahren habe, .init() auf Class.self zu nennen, hat es so viele Türen geöffnet! Ich stimme zu, dass Ihre Herangehensweise am wenigsten komplex ist, aber ich fühle mich genötigt, Nates Antwort als akzeptierte Antwort zu wählen, da er genau das beantwortet hat, wonach ich gefragt hatte, und es funktioniert auch einwandfrei! – gokeji

Verwandte Themen