2013-02-20 37 views
4

我經常使用類工廠模式,即類有一個私有構造函數和一個靜態方法來創建類。這允許出於某種原因無法構造類的情況,並返回null - 非常方便。如何使類工廠創建所需的派生類

我希望能夠擴展到一個工廠方法,它根據條件從派生類的層次結構中創建一個特定的類。然而,我看不到一種方法,然後隱藏派生類的構造函數強制使用工廠方法。如果工廠方法在基類中,則不再有權訪問派生類的私有構造函數。將工廠方法放在每個派生類中都不起作用,因爲事先必須知道所需的類型。如果一個類可以訪問嵌套類的私有成員,嵌套類可能是一種方式,但可惜的是,似乎嵌套類可以訪問封閉類的私有成員,但不是相反。

有沒有人知道這樣做的方式?

回答

1

這裏的示例代碼我工作的時候丹尼爾張貼了他的答案。它看起來像它在做什麼,他建議:

public static class BaseFactory 
{ 
    public static Base Create(bool condition) 
    { 
     if (condition) 
     { 
      return Derived1.Create(1, "TEST"); 
     } 
     else 
     { 
      return Derived2.Create(1, DateTime.Now); 
     } 
    } 
} 

public class Base 
{ 
    protected Base(int value) 
    { 
    } 

    protected static Base Create(int value) 
    { 
     return new Base(value); 
    } 
} 

public sealed class Derived1: Base 
{ 
    private Derived1(int value, string text): base(value) 
    { 
    } 

    internal static Derived1 Create(int value, string text) 
    { 
     return new Derived1(value, text); 
    } 
} 

public sealed class Derived2: Base 
{ 
    private Derived2(int value, DateTime time): base(value) 
    { 
    } 

    internal static Derived2 Create(int value, DateTime time) 
    { 
     return new Derived2(value, time); 
    } 
} 

[編輯],丹尼爾的第二個建議:

public static class BaseFactory 
{ 
    public static Base Create(bool condition) 
    { 
     if (condition) 
     { 
      return new Derived1Creator(1, "TEST"); 
     } 
     else 
     { 
      return new Derived2Creator(1, DateTime.Now); 
     } 
    } 

    private sealed class Derived1Creator: Derived1 
    { 
     public Derived1Creator(int value, string text): base(value, text) 
     { 
     } 
    } 

    private sealed class Derived2Creator: Derived2 
    { 
     public Derived2Creator(int value, DateTime time): base(value, time) 
     { 
     } 
    } 
} 

public class Base 
{ 
    protected Base(int value) 
    { 
    } 

    protected static Base Create(int value) 
    { 
     return new Base(value); 
    } 
} 

public class Derived1: Base 
{ 
    protected Derived1(int value, string text): base(value) 
    { 
    } 

    protected static Derived1 Create(int value, string text) 
    { 
     return new Derived1(value, text); 
    } 
} 

public class Derived2: Base 
{ 
    protected Derived2(int value, DateTime time): base(value) 
    { 
    } 

    protected static Derived2 Create(int value, DateTime time) 
    { 
     return new Derived2(value, time); 
    } 
} 

注意這第二種方法意味着,類不能被密封,很遺憾。

+0

丹尼爾和馬修。真的很聰明。它工作並滿足所有要求。我喜歡。我只是不知道誰的答案是接受的:-) – Dave 2013-02-20 12:36:16

+0

馬修,第一個代碼並不是我的第一個選項。正如我所說,使*構造函數*'內部'。使用其他工廠方法繞道不會帶來任何好處。 – 2013-02-20 12:40:01

+0

贊成。這只是爲了演示如何使用工廠方法 - 儘管工廠方法在這種情況下非常簡單,以至於它不會隱藏任何東西。在一般情況下,它可以。 – 2013-02-20 12:58:09

0

而不是在類本身內部使用方法作爲工廠通過靜態類(「工廠」)實現工廠模式,該靜態類根據您編寫的邏輯返回正確的實例。

+0

這並不解決問題,這就是:「從創建實例防止用戶通過'new'「。但我同意建築應該留給第三班。基類不應該知道從它派生的類。 – 2013-02-20 10:23:55

+0

@DanielHilgarth你是對的,因爲他應該將另一個項目中的類型作爲內部來分開,並通過接口公開它們。 – dutzu 2013-02-20 10:28:22

+0

請重新閱讀該問題。我引用:「然而,我看不到一種方法,然後隱藏派生類的構造函數強制使用工廠方法」。 – 2013-02-20 10:29:09

4

有幾種可能性,其中兩個是:

  1. 把所有這些類中的一個項目,使構造internal。其他項目將無法調用這些構造函數,但該項目中的代碼可以。
  2. 製作這些類的構造函數protected(而不是private),並在包含工廠方法的類中創建一個私有派生類。創建該私有類的實例並將其返回。
  3. 第二種選擇

例子:

public static class AnimalFactory 
{ 
    public static Animal Create(int parameter) 
    { 
     switch(parameter) 
     { 
      case 0: 
       return new DogProxy(); 
      case 1: 
       return new CatProxy(); 
      default: 
       throw new ArgumentOutOfRangeException("parameter"); 
     } 
    } 

    private class DogProxy : Dog { } 

    private class CatProxy : Cat { } 
} 

public abstract class Animal { } 

public class Dog : Animal 
{ 
    protected Dog() { } 
} 

public class Cat : Animal 
{ 
    protected Cat() { } 
} 
+0

丹尼爾和馬修。真的很聰明。它工作並滿足所有要求。我喜歡。我只是不知道誰的答案被接受爲:-) – Dave 2013-02-20 12:35:34

+0

@Dave:你必須爲自己決定。通常你會接受最能幫助你的答案。如果有多個答案幫助你平衡你,你可以不接受任何一個,也不接受你更喜歡或最先發布的答案。 – 2013-02-20 12:37:36

0

可以攔截派生類型建立在基類構造器,並檢查來電者是你的工廠使用StackFrames:

protected Class1() //base class ctor 
    { 
     StackFrame[] stackFrames = new StackTrace().GetFrames(); 
     foreach (var frame in stackFrames) 
     { 
      //check caller and throw an exception if not satisfied 
     } 
    } 
相關問題