2016-01-12 142 views
5

我想創建一個工廠函數,它接受一個類類型並返回一個構造函數,以便我可以使用該構造函數稍後將創建該類的實例。Swift:創建一個將類類型作爲參數的工廠函數,並輸出該類的構造函數

想象一下,我有兩個類,蘋果和橙,這兩個類都是水果的子類。他們需要用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功能,你需要聲明typeOfFruit參數作爲Fruit超的元類型,並明確引用初始化:

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

只能訪問一元類型required初始化,使init需要被這樣標記:

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

其餘的應該只是工作:

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

這是一個很好的答案。該方法似乎很簡單,但也需要了解Swift的對象創建模型的許多小特性。我現在正在嘗試,如果它能正常工作,它會很快讓你知道! – gokeji

0

你可以聲明Fruit爲公開要求init方法的協議,並利用泛型支持在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爲這個特定的類,並傳遞一個默認的顏色(或者一個計算的,取決於工廠的規格)。這不會失去使用Swift增長的類型安全性。

+1

親愛的選民!請讓我們知道,你在這個答案中看到了什麼錯誤。所有的答案(Nate的,Cristik的廣告Alain的)基本上使用相同的方法,並且所有的答案都僅在小細節上有所不同。爲什麼Cristik的答案是低票?請!如果有人不喜歡一些答案,讓我們知道原因! – user3441734

+0

嗨Cristik,非常感謝你的深思熟慮的答案。我以前沒有考慮過使用泛型的方法,你的答案肯定會教給我一些東西。我已經接受了Nate的回答,因爲它以非常簡單的方式提供了我所要求的內容,但我也非常感謝您的意見。 對不起,有人低估了這個答案;我已經提高了它。 – 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()的可能性,它就打開了這麼多門! 我同意你的方法是最簡單的,但我覺得不得不選擇Nate的答案作爲接受的答案,因爲他確切地回答了我所要求的,並且它的工作也完美無缺! – gokeji

相關問題