2011-12-21 53 views
5

此問題可能已發佈,但我找不到它。接口或switch語句,找到正確的模式

我一直在寫這類東西很久,我坐下來寫新的東西,只是開始輸入這個,就好像它是我自己的模式。最近一個項目出現了,我發現自己正在查看自己的代碼並開始考慮它看起來有多臭。

BackgroundInfoIfYouCare 

在這個特定的圖書館,我需要發送電子郵件給用戶。到目前爲止,有13封電子郵件。

每封電子郵件都有它自己的模板(我使用的是剃刀分析器,所以模板是用cshtml編寫的)。 每個電子郵件模板都有一個字符串的名稱鍵。 每封電子郵件都有自己的EF4查詢,以基於「成員資格」實體和所有相關數據返回模型。

我有一個類接受一個字符串,它是一個電子郵件模板名稱鍵。

該方法將運行適當的查詢並獲取列表,獲取電子郵件模板。

將列表和模板傳遞給解析器,將每個成員關係合併到模板並返回列表電子郵件。

EndOfBackgroundInfoIfYouCare 

所以真正的問題......這樣做的最好方法是什麼?

一種方法是隻使用一個開關

public List<Membership> Execute(string TemplateKey) { 
switch (TemplateKey) 
     { 
      case "SomethingExpired": 
       QueryResult = new SomethingExpiredEmailQuery().ExecuteQuery(); 
       break; 
      case "SomethingExpireIn30": 
       QueryResult = new SomethingExpireIn30EmailQuery().ExecuteQuery(); 
       break; 
      case "FirstTimeLoginThanks": 
       QueryResult = new FirstTimeLoginThanksEmailQuery().ExecuteQuery(); 
       break; 
      case "SecurityTraining": 
       QueryResult = new SecurityTrainingEmailQuery().ExecuteQuery(); 
       break; 
      case ETC ETC ETC... 

}

另一種方法是使用一個接口

IEmailQuery 
void ExecuteQuery() 

但是,如果使用的界面我仍然需要實例化Query類。它不會節省代碼,也不會使代碼更易於維護。

有了反思,我可以做一些事情,如使用模式命名所有電子郵件查詢: 電子郵件模板SecurityTraining的密鑰的查詢名稱爲SecurityTrainingEmailQuery,我可以使用反射來實例化和調用ExecuteQuery方法。

沒有使用反射,有沒有更清潔的方式來接線?

回答

3

其實這對我來說看起來不太臭。如果你不喜歡switch-statement,你可以去IEmailQuery-Path,然後在Dictionary<string,IEmailQuery>上連線。 這可能節省了一些代碼行,你可以像訪問:

QueryDictionary["MyKey"].ExecuteQuery(); 

乾杯, 奧利弗

+0

那麼,Jon的回答是類似的,只有這樣才能更先進,然後我纔會害怕;-) – Lindan 2011-12-21 14:07:03

7

一個選項是有一個Dictionary<string, Func<IEmailQuery>>地圖。你可以這樣構建它:

private static readonly Dictionary<string, Func<IEmailQuery>> MailQueryMap = 
    new Dictionary<string, Func<IEmailQuery>> { 
    { "SomethingExpired",() => new SomethingExpiredMailQuery() }, 
    { "SomethingExpireIn30",() => new SomethingExpireIn30EmailQuery() }, 
    // etc 
}; 

然後:

public List<Membership> Execute(string templateKey) { 
    IEmailQuery query = MailQueryMap[templateKey].Invoke(); 
    var queryResult = query.ExecuteQuery(); 
    // ... 
} 

如果你能保證,你永遠只需要無參數的構造函數,你總是可以存儲一個Dictionary<string, Type>並通過反射實例 - 但會有一些醜陋的演員等。

編輯:當然,如果該模板的名稱總是類型的名稱,你可以使用

Type queryType = Type.GetType(namespacePrefix + "." + templateKey); 
IEmailQuery query = (IEmailQuery) Activator.CreateInstance(queryType); 
var queryResult = query.ExecuteQuery(); 

您還可以嘗試使用枚舉而不是魔法考慮字符串常量。

+0

這是如何遵循打開/關閉原則?如果我正確閱讀Paul的帖子,他想避免必須改變現有的類(用新案例擴展switch語句)。 – Wivani 2011-12-21 14:55:17

+0

@Wivani:我沒有看到任何暗示 - 我只看到他希望代碼更簡單,更易於維護。在這個問題中,它是否談論避免改變現有的類? – 2011-12-21 14:56:36

+0

猜猜我正在製作'你和我的屁股';-)讓我們來看看保羅能否證實我認爲他正在尋找的東西。 – Wivani 2011-12-21 15:05:43

0

爲什麼不在你的問題中使用反射?我認爲它是做這種事情的有效方式。

另一種方法是使用控制/依賴注入模式的反轉。你可以像你一樣定義一個接口,並將所有已知的具體實現註冊到DI容器中(這可以通過配置或代碼完成)。

註冊時,您需要告訴DI容器一些服務名稱來區分實現,因爲它們實現相同的接口。

YourIocContainer.Register<IEmailQuery>(typeof(SomethingExpiredMailQuery), 
             "SomethingExpiredMailQuery"); 

實例化時,可以通過再次提供服務名稱得到相應的落實:

public List<Membership> Execute(string TemplateKey) { 
    YourIocContainer.Resolve<IEmailQuery>(TemplateKey); 
+0

當我想到在stackoverflow上提問時,我首先想到的是反射。速度不是問題,所以它將是一個有效的方法來使用。我只是好奇其他解決方案可能在那裏,我沒有想到。我也喜歡你的依賴注入解決方案。 – 2011-12-22 04:12:12

1

我會去工廠模式,像

class EmailQueryFactory 
{ 
    public IEmailQuery Create(String TemplateKey) 
    { 
    .... 
    } 
} 

,然後

//.. first get String TemplateKey 

IEmailQuery qry=EmailQueryFactory.Create(TemplateKey); 
qry.Execute(); 
+0

我正在考慮使用工廠,但隨後在工廠中最終出現同樣的問題。您需要將請求的模板連接到適當的Query類。 – 2011-12-22 03:29:38

0

命令模式是用於此場景的完美模式。請參閱http://www.codeproject.com/KB/books/DesignPatterns.aspx以瞭解該模式的練習c#說明。 Jon Skeet描述的Lambdas是有用的更新的編程結構,您可以看到。有關使用該模式的更多討論,請參閱Command Pattern : How to pass parameters to a command?

+0

感謝您提及命令模式。迫使我重讀4gang書中的定義。我很好奇,但是通過它的使用我會得到什麼。我是否仍然需要創建一個接收器實例來調用該命令?帶我回到一個類的列表,並將請求的命令與正確的命令相關聯。 – 2011-12-22 04:08:54