2016-01-12 1 views
5

クラス型を取り入れてコンストラクタを返すファクトリ関数を作成したいので、後でそのクラスのインスタンスを作成するコンストラクタです。Swift:クラス型をパラメータとし、そのクラスのコンストラクタを出力するファクトリ関数を作成する

私はフルーツのサブクラスであるAppleとOrangeの2つのクラスがあるとします。それらは私が後で知るだけのunknownNumberで初期化する必要があります。

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

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

私は後でunknownNumberで、この関数を呼び出すとフルーツの特定のサブクラスを初期化することができるように、クラスタイプにとるファクトリ関数を作成したいと思います。

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

そして、私が行うことができ、orangeFactoryを作成するには:

let orangeFactory = makeFruit(Orange)  

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

私は単にunknownNumber怠惰な変数を作成するためのオプションを認識していますが、私の特定の場合にはunknownNumberだけではなく、数とそれには他のプロセスが含まれているので、構造を簡単に保つために、すべてのものを用意したらオブジェクトを作成したいと思います。

これはSwiftでも可能ですか?私はオンラインでしばらくの間研究してきましたが、直接的な答えは見つけられませんでした。どんな助けでも大歓迎です!

答えて

4

逆に作業してみましょう。あなたのmakeFruit機能では、あなたFruitスーパークラスのメタタイプとしてtypeOfFruitパラメータを宣言して、明示的に初期化を参照する必要があります:

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

initニーズがあることをするようにあなたは、メタタイプのrequired初期化子にのみアクセスすることができますそのようにマーク:

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

残りはちょうど動作するはずです:

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

これは優れた答えです。この方法はとてもシンプルなようですが、Swiftのオブジェクト作成モデルの多くの小さな特質についての知識も必要です。私は今これを試しているし、それがうまくいくとすぐにあなたに知らせるでしょう! – gokeji

0

あなたは必要なinit方法を公開プロトコルとしてFruitを宣言し、Switfにおけるジェネリック医薬品のサポートを利用することができます:

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 

makeFruit関数の結果が同じであるように、これはまた、あなたが安全を入力しますclsパラメータで指定された型。

これは工場出荷時の機能ではなく、単なる転送機能です。しかし、あなたはさらに行くと果物の一部についてmakeFruitをカスタマイズし、そしてこれはファクトリ関数作っているものであることができます。

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 

Appleクラスは色を受け入れる初期化子を持って、我々はオーバーライドできると仮定するとmakeFruitこの特定のクラスについては、デフォルトの色を渡してください(または、工場の仕様に応じて、計算された色を渡すこともできます)。これは、スイフトであなたに成長するタイプの安全性を失うことなく。

+1

親愛なるダウン投票者!私たちに知らせてください、あなたはこの答えで間違っていますか?すべての答え(Nate's、Cristikの広告Alain's)は、基本的に同じアプローチを使用しており、すべてが小さな詳細でのみ異なります。なぜクリスティクの答えがダウン投票されましたか?お願いします!誰かが何らかの答えを気に入らない場合は、理由を教えてください! – user3441734

+0

こんにちはクリスティク、ありがとう、思慮深い答えです。以前はジェネリックを使ったアプローチは考えていなかったので、あなたの答えは確かに私にいくつか教えてくれました。私はネイトの答えを受け入れてくれました。私が求めていたものを、まっすぐ前向きに提供してくれましたが、本当にありがとうと思います。 申し訳ありません、誰かがこの回答を下落させました。私はそれをupvotedしています。 – gokeji

+1

ダウン投票についての心配はありません。私はそうではありません:) – Cristik

1

ファクトリエントリの識別子としてクラス自体を使用する場合は、実際にはファクトリは必要ありません。ファクトリパターンは、任意の識別子とオブジェクトの対応するクラスとの間の間接参照を作成します。フルーツクラスでのご初期化子が動作するために、このために必要としてマークする必要が

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) 

注:

スウィフトでこれを行う簡単な方法は、辞書を使用することです。

+0

お返事ありがとうございました。 Class.selfで.init()を呼び出す可能性について学ぶと、とても多くの扉が開けました! 私はあなたのアプローチが最も複雑ではないことに同意しますが、彼は私が求めていたものに正確に答えたので、ネイトの答えを受け入れられた答えとして選択するように強く感じ、それは完璧に機能します! – gokeji

関連する問題