0

遵循SO和其他地方的建議,我嘗試使用接口來實現多態,如下面的簡化示例所示。對於從接口繼承的類型,可以有多態工廠嗎?

type IFace = 
    // abstract methods 
    abstract member foo: int -> int 

type X(i: int) = 
    // some code 
    interface IFace with 
     member this.foo j = i + j 

type Y(s: string) = 
    // some code 
    interface IFace with 
     member this.foo j = (int s) + j 

type Z(x: float) = 
    // some code 
    interface IFace with 
     member this.foo j = (int x) + j 

這些值可通過X,Y的構造函數中使用,並且Z:

let zX = 1 
let zY = "2" 
let zZ = 3.0 

我想建立一個多態工廠像

let create (str: string) = 
    match str with 
    | "X" -> X(zX) 
    | "Y" -> Y(zY) 
    | "Z" -> Z(zZ) 
    | _ -> failwith "Something went wrong" 

let XObj = create "X"  
let YObj = create "Y" 
let ZObj = create "Z" 

然而,create會不會編譯,因爲它會返回不同類型的對象。

一個替代方案是上溯造型任何情況下都create到System.Object的:

let create1 (str: string) = 
    match str with 
    | "X" -> X(zX) :> System.Object 
    | "Y" -> Y(zY) :> System.Object 
    | "Z" -> Z(zZ) :> System.Object 
    | _ -> failwith "Something went wrong" 

然後create1將編譯,但它的返回值將(我相信)是無用的,除非垂頭喪氣到X,Y或Z但是這再次引發了多態性問題:一般情況下,我需要一個返回不同類型值的函數。

另一種替代方法是使用可識別聯合

type ClassDU = 
    | XClass of X 
    | YClass of Y 
    | ZClass of Z 

,並使用上面定義的函數create。但那不行。如果沒有向上轉換,編譯器仍然會將不同的情況視爲具有不同的類型。由於ClassDU不是類,因此向ClassDU上傳不起作用。

在此代碼段

http://fssnip.net/k6/title/-F-Factory-Pattern

的問題是使用一個抽象類XYZ從其中X,Y,和Z將繼承抽象方法foo解決。類似於create功能會上溯造型各種情況下的抽象類:

let create2 (str: string) = 
    match str with 
    | "X" -> X(zX) :> XYZ 
    | "Y" -> Y(zY) :> XYZ 
    | "Z" -> Z(zZ) :> XYZ 
    | _ -> failwith "Something went wrong" 

是否有可能建立一個多態工廠類型從接口繼承或沒有一個必須創建一個抽象類中的代碼片段?

回答

4

接口

要使用接口做到這一點,你只需要上溯造型到接口的類型,例如:

let create (str: string) = 
    match str with 
    | "X" -> X(zX) :> IFace 
    | "Y" -> Y(zY) :> IFace 
    | "Z" -> Z(zZ) :> IFace 
    | _ -> failwith "Something went wrong" 

這有類型簽名:

val create : str:string -> IFace 

個Disciminated工會

要與下游用戶做到這一點,你需要添加appriopriate情況下構造在create功能。

type ClassDU = 
    | XClass of X 
    | YClass of Y 
    | ZClass of Z 

let createDU (str: string) = 
    match str with 
    | "X" -> XClass <| X(zX) 
    | "Y" -> YClass <| Y(zY) 
    | "Z" -> ZClass <| Z(zZ) 
    | _ -> failwith "Something went wrong" 

這有類型簽名:

val createDU : str:string -> ClassDU 

我覺得周圍的歧視工會的行爲,你誤解的事實,你想他們的作爲像抽象類的莖多個小類,將它們想象成具有多個構造函數的單一類型會更好和更準確。

+0

我按照你的建議接口,當然它的工作。兩個問題。首先,在我用'create'函數構造我的對象之後,一些upcasts變得不必要了,編譯器開始給出警告。我現在處理的對象與使用常規類構造函數之前創建的對象有什麼不同?如果是這樣的話,我會失去一些功能嗎?第二:如果我的類繼承了幾個接口,並且「只創建」其中一個接口,會發生什麼?我可以同時上傳到多個界面嗎? – Soldalma