2011-12-21 42 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