更换大开关?

时间:2012-02-21 16:21:41

标签: c# asp.net .net design-patterns

我有一个名为“ReportController.aspx”的页面,其目的是根据查询字符串参数实例化报表(类)

        switch (Request.QueryString["Report"])
        {                
            case "ReportA":
                CreateReportAReport("ReportA's Title");
                break;
            case "ReportB":
                CreateReportBReport("ReportB's Title");
                break;                
            case "ReportC":
                CreateReportCReport("ReportC's Title");
                break;
            case "ReportD":
                CreateReportDReport("ReportD's Title");
                break;
                ...

基本上,每次需要新报告时,都会有添加案例和添加方法的开销。这个switch语句可能会变得非常长。我读过可以使用Dictionary将报告映射到?。如何使用字典(假设这是一种更好的方式)。

此外,CreateReportXReport方法基本上将一堆额外的QueryString值传递给报表类的构造函数(每个报表类都有不同的构造函数)。

4 个答案:

答案 0 :(得分:6)

没有必要在某处输入新信息;关键是要将其从代码中删除,以避免重新编译和重新部署以进行这种微不足道的更改。

一些好的选择是在XML配置文件中列出这些值,或者更好的是在数据库中列出。

您可能希望用这些数据填写字典,无论来源如何。这将:

  • 轻松缓存
  • 制作干净,快速的代码

当需要将数据从配置中提取到代码中时,您需要将项目添加到字典中,如下所示:

Dictionary<string, IReportCreator> = configDataGetter.GetReportDataFromDB().
    ToDictionary(r => r.Name, myReportCreatorFactory(r => r.ReportID))

此示例假定您将数据作为某种实体对象获取,并使用工厂将strategy pattern用于创建报告的代码。当然,你有多种方法可以做到这一点。

我认为报告过于广泛,多样,性质不同,你不能只在数据库中放置sql和样式构建块?

根据op的评论

修改

啊,好吧。好吧,我不知道你有多少时间,但是就像你把所有东西都推到某种factory那样,你有更好的选择。我将从一些类似的事情中给你一些希望有所帮助的想法。每个步骤本身都是一个改进,但也是一个很好的步骤,以真正将报告逻辑与此shell代码分开。此外,我可以看到你已经知道你正在做什么,我肯定知道下面我会说些什么,但我不知道你知道什么,这对其他人有帮助。

首先,将所有信息从代码中提取到数据库(如果还没有),并且在改进设置时添加更多数据库字段(以及一两个表)。

你可能已经知道了,但我会为其他人提一下,看看我上面提到的策略模式。您可以将每个“报表函数”的自定义逻辑实际放在各种策略类的构造函数中。它们都将从您的基础ReportGenerator继承(或使用常见的IReportGenerator接口)。他们可以而且应该共享相同的构造函数;变量报告参数将由字典类型的参数处理。每个类的构造函数实现都知道变量的类型是需要的(来自db配置),并相应地转换/使用它们。

下一步可能是使用reflection真正摆脱工厂中的select语句。您必须将该类的名称作为db中报告配置数据的一部分(并且具有公共构造函数)。

此时,添加新报告的方式非常简洁,即使您每次都必须添加新类。那么好。它符合single responsibilityopen-closed原则。

现在,只有从您的应用中删除课程的最后一步,因此可以动态添加/编辑它们。查看MEF。这就是它的目的。你可能在不应该使用的互联网上找到的一些东西是CodeDom(当没有别的东西时很好,但MEF更好)和编译即服务.NET中提供的功能5. MEF是最佳选择。

答案 1 :(得分:4)

假设所有报告都实施IReport,您可以使用Func<IReport>执行此操作,如下所示:

IDictionary<string,Func<IReport>> dictToReport = new Dictionary {
    {"ReportA", () => CreateReportAReport("ReportA's Title") }
,   {"ReportB", () => CreateReportBReport("ReportB's Title") }
,   ...
};

然后您可以使用以下代码替换开关:

var myReport = dictToReport[Request.QueryString["Report"]]();

答案 2 :(得分:1)

我认为最好重新设计此代码并将其转换为某些数据库表(“报告”),以保留每个报告的报告列表和ID。

就是这样。

答案 3 :(得分:1)

要使用Dictionary<string, string>执行此操作,您只需在包含类型中将其构建为静态缓存

public class Container {
  private static Dictionary<string, Func<Report>> ReportMap = 
    new Dictionary<string, Func<Report>>();
  static Container() {
    ReportMap["ReportA"] = () => CreateReportAReport("ReportA's Title");
    ReportMap["ReportB"] = () => CreateReportBReport("ReportB's Title");
    // etc ...
  }
}

既然构建了地图,您只需在函数中进行查找,而不是switch

Func<Report> func;
if (!ReportMap.TryGetValue(Request.QueryString["Report"), out func)) {
  // Handle it not being present
  throw new Exception(..);
}

Report report = func();