2012-02-10 47 views
2

我在應用程序中有一些代碼,我現在不太感到興奮。我創建了一些類,像這樣:基於字符串屬性根據一些標準創建不同的類型

class Base 
{ 
    // base properties ... 
} 

class DerivedA : Base 
{ 
} 

class DerivedB : Base 
{ 
} 

我在我的應用程序,需要創建這些對象之一(有更多的驚喜在未來)的方法存儲在數據庫中。這些對象中的每一個從稍微不同的地方獲取數據,但如果顯得塊,它不很維護我這樣做是正確的,現在的方式是隻是一個大:

class BaseCreator 
{ 
    Base Create(string name) 
    { 
     if (name == "DerivedA") 
      return CreateDerivedA(); 
     else if(name == "DerivedB") 
      return CreateDerivedB(); 
    } 
} 

有什麼辦法我可以重構此代碼以使其更易於維護,並且可以更輕鬆地在將來添加新類型?我在我的應用程序中使用依賴注入(Ninject),如果這有什麼區別的話。

回答

1

繼承樹一旦成長就難以維持。如果你知道樹會很大 - 認真考慮使用組合而不是繼承。特別是如果你已經使用了DI框架,接口就是要走的路。

+0

我從未見過超過十二種類型被添加,但您能提供一個您的意思的例子。我沒有看到我將它轉換爲使用DI容器和基於字符串構造對象之間的明顯聯繫。 – 2012-02-10 20:57:37

+0

轉換應該是從一個類到一個接口的基礎。每種類型的Base都可以實現這個接口,並且這允許這些實現和合約(在你的情況下爲Base)可以根據接口的需求進行管理。字符串,使枚舉或更少的錯誤容易。讓您的DI容器將類型(基於您的新枚舉)綁定到基礎合同,並讓DI容器解析返回您的目標界面。 – OnResolve 2012-02-10 21:04:23

+0

該字符串存儲在數據庫中。 – 2012-02-10 21:11:25

1

如果你真的必須使用字符串,你可以使用反射:

object GetInstance(string typeName) 
{ 
    Type.GetType(typeName).GetConstructor(Type.EmptyTypes).Invoke(new object[0]); 
} 

你也可以使用字典:

IDictionary<string, Func<object>> TypeMap = new Dictionary<string, Func<object>>() 
{ 
    { "TypeA",() => new TypeA() }, 
    { "TypeB",() => new TypeB() }, 
    { "TypeC",() => new TypeC() }, 
}; 

object GetInstance(string typeName) 
{ 
    return TypeMap[typeName](); 
} 

對於其他人登陸這個頁面上,可以考慮使用泛型,如果你不使用字符串:

T CreateInstance<T>() 
    where T : new() 
{ 
    return new T(); 
} 
+0

@GuiltShow是的,這就是爲什麼我提供了另外兩種使用字符串的方法。記住,我不只是回答你。我正在回答登陸此頁面的其他人。 – Zenexer 2012-02-10 21:06:22

+0

@GuiltShow如果它讓你更快樂,我會在答案中明確地說出來。啊,對不起。我通常張貼我的答案。 – Zenexer 2012-02-10 21:07:50

+0

@GuiltShow現在應該更好地措辭。 – Zenexer 2012-02-10 21:09:10

1

我會注意到,你的if/elseswitch結構是不是一件壞事。不好的是當你有相同的if/elseswitch多次表達

當你已經很好地分離你的代碼和你是編程的接口或抽象的基礎,而不是具體的,知道某處在您的應用程序東西知道如何創建你所需要的特定的具體實例。這可以是代碼,它可以是配置,也可以是一些容器等。但是,東西必須存在。這個想法是有東西現有一次。

只要這是它存在的唯一方法,您的方法就沒有問題。這個類存在的理由是它創建了滿足某些接口的具體實例。其改變的原因是其他一些具體的實現已經被添加(或刪除)。

1

一般情況下,可以由位的組合物和使用說明書圖案的解決:

public class Base 
{ 
    public abstract bool IsSatisfiedBy(string name); 

    // base properties ... 
} 

public class DerivedA : Base 
{ 
    public override bool IsSatisfiedBy(string name) 
    { 
     return name == "DerivedA"; 
    } 
} 

public class DerivedB : Base 
{ 
    public override bool IsSatisfiedBy(string name) 
    { 
     return name == "DerivedB"; 
    } 
} 

public class BaseCreator 
{ 
    private readonly IEnumerable<Base> candidates; 

    public BaseCreator(IEnumerable<Base> candidates) 
    { 
     this.candidates = candidates; 
    } 

    public Base Create(string name) 
    { 
     return this.candidates.First(c => c.IsSatisfiedBy(name)); 
    } 
} 
+0

有一本字典可能會更容易,但我認爲它在理論上的效率是相同的。 – Zenexer 2012-02-10 21:12:10

+0

在這種特殊情況下,它看起來等同於字典,但由於您可以將任何內容放入IsSatisfiedBy實現中,因此您可以創建更多基於約定或算法的解決方案。出於這個原因,這個解決方案比字典更靈活。 – 2012-02-10 21:39:10

+0

很好的答案,但它需要你持有DerivedA和DerivedB的預先實例化的類的列表,並且Create的語義有些破碎,因爲你不是創建而是返回一個與謂詞條件相匹配的現有實例,這可能會引起誤解。 – Anastasiosyal 2012-02-15 09:55:15

0

沒有一般的這個問題的答案。抽象工廠可能是正確的,但這完全取決於這些實現與您如何使用它們之間的區別。

很可能你應該使用模板,策略,狀態或任何其他類似的模式。看看它們,並確定抽象工廠,並決定適合您的特定場景的模式。

相關問題