C#:抽象策略基类,用作策略对象的抽象工厂
我正在尝试为我的公司创建一个基于网络的工具,本质上,它使用地理输入生成表格结果。目前,三个不同的业务领域使用我的工具并收到三种不同的输出。幸运的是,所有输出都基于主表-子表的相同思想,它们甚至共享一个公共主表 不幸的是,在每种情况下,子表的相关行都包含截然不同的数据。因为这是唯一的争论点,所以我将C#:抽象策略基类,用作策略对象的抽象工厂,c#,factory-pattern,strategy-pattern,C#,Factory Pattern,Strategy Pattern,我正在尝试为我的公司创建一个基于网络的工具,本质上,它使用地理输入生成表格结果。目前,三个不同的业务领域使用我的工具并收到三种不同的输出。幸运的是,所有输出都基于主表-子表的相同思想,它们甚至共享一个公共主表 不幸的是,在每种情况下,子表的相关行都包含截然不同的数据。因为这是唯一的争论点,所以我将FetchChildData方法提取到一个名为DetailFinder的单独类中。因此,我的代码如下所示: DetailFinder DetailHandler; if (ReportType == "
FetchChildData
方法提取到一个名为DetailFinder
的单独类中。因此,我的代码如下所示:
DetailFinder DetailHandler;
if (ReportType == "Planning")
DetailHandler = new PlanningFinder();
else if (ReportType == "Operations")
DetailHandler = new OperationsFinder();
else if (ReportType == "Maintenance")
DetailHandler = new MaintenanceFinder();
DataTable ChildTable = DetailHandler.FetchChildData(Master);
DetailFinder DetailHandler = DetailFinder.Parse(ReportType);
其中PlanningFinder、OperationsFinder和MaintenanceFinder都是DetailFinder的子类
我刚刚被要求为另一个业务领域添加支持,如果阻止趋势,我不想继续这种if
block趋势。我更希望有一个如下所示的解析方法:
DetailFinder DetailHandler;
if (ReportType == "Planning")
DetailHandler = new PlanningFinder();
else if (ReportType == "Operations")
DetailHandler = new OperationsFinder();
else if (ReportType == "Maintenance")
DetailHandler = new MaintenanceFinder();
DataTable ChildTable = DetailHandler.FetchChildData(Master);
DetailFinder DetailHandler = DetailFinder.Parse(ReportType);
但是,我不知道如何让
DetailFinder
知道哪些子类处理每个字符串,甚至知道存在哪些子类,而不只是将if块移动到Parse
方法。子类是否有办法在抽象的DetailFinder
中注册自己?只要大的语句阻塞或开关
语句或任何它只出现在一个地方的东西,这对可维护性并没有坏处,所以不必为此担心
然而,当谈到可扩展性时,情况就不同了。如果您真的希望新的DetailFinder能够注册自己,您可能需要查看基本上允许您将新程序集放入“加载项”文件夹或类似文件夹中的,然后核心应用程序将自动拾取新的DetailFinder
但是,我不确定这是否是您真正需要的扩展性。您可能希望使用类型到创建方法的映射:
public class DetailFinder
{
private static Dictionary<string,Func<DetailFinder>> Creators;
static DetailFinder()
{
Creators = new Dictionary<string,Func<DetailFinder>>();
Creators.Add( "Planning", CreatePlanningFinder );
Creators.Add( "Operations", CreateOperationsFinder );
...
}
public static DetailFinder Create( string type )
{
return Creators[type].Invoke();
}
private static DetailFinder CreatePlanningFinder()
{
return new PlanningFinder();
}
private static DetailFinder CreateOperationsFinder()
{
return new OperationsFinder();
}
...
我不确定这是否比您的if语句好很多,但它确实使它易于阅读和扩展。只需在Creators
映射中添加一个创建方法和条目
另一种选择是存储报表类型和查找器类型的映射,如果您总是简单地调用构造函数,则在该类型上使用Activator.CreateInstance。如果对象的创建更加复杂,上面的工厂方法细节可能更合适
public class DetailFinder
{
private static Dictionary<string,Type> Creators;
static DetailFinder()
{
Creators = new Dictionary<string,Type>();
Creators.Add( "Planning", typeof(PlanningFinder) );
...
}
public static DetailFinder Create( string type )
{
Type t = Creators[type];
return Activator.CreateInstance(t) as DetailFinder;
}
}
公共类详细信息查找器
{
私有静态词典创建者;
静态DetailFinder()
{
创建者=新字典();
添加(“计划”,typeof(PlanningFinder));
...
}
公共静态DetailFinder创建(字符串类型)
{
类型t=创建者[类型];
返回Activator.CreateInstance(t)作为DetailFinder;
}
}
为了避免不断增长的if..else块,您可以将其切换,以便各个查找程序向factory类注册它们处理的类型
初始化时的工厂类需要发现所有可能的查找程序,并将它们存储在hashmap(字典)中。这可以通过反射和/或使用MarkSeemann建议的托管可扩展性框架来实现
但是,要小心,不要让这过于复杂。更愿意做最简单的事情,现在就可以工作,以便在需要时进行反射。如果您只需要再多一个finder类型,请不要构建复杂的自配置框架;) 您可以使用反射。
DetailFinder的Parse方法有一个示例代码(请记住在该代码中添加错误检查):
方法GetDetailFinderClassNameByReportType
可以从数据库、配置文件等获取类名
我认为有关“插件”模式的信息在您的情况下会很有用:您可以使用IoC容器,其中许多容器允许您使用不同的名称或策略注册多个服务
例如,使用假设的IoC容器,您可以执行以下操作:
IoC.Register<DetailHandler, PlanningFinder>("Planning");
IoC.Register<DetailHandler, OperationsFinder>("Operations");
...
国际奥委会注册(“规划”);
国际奥委会注册(“运营”);
...
然后:
DetailHandler handler = IoC.Resolve<DetailHandler>("Planning");
DetailHandler=IoC.Resolve(“计划”);
关于这个主题的一些变化
您可以查看以下IoC实现:
正如马克所说,一个大的if/开关块并不坏,因为它将全部放在一个地方(所有的计算机科学基本上都是关于在某种空间中获得相似性)
也就是说,我可能只会使用多态性(从而使类型系统适合我)。让每个报表实现一个FindDetails方法(我会让它们从报表抽象类继承),因为您将以几种细节查找器结束。这还模拟了函数式语言中的模式匹配和代数数据类型。出于好奇,如果创建者受到保护,DetailFinder的子类是否可以在自己的静态构造函数中将自己添加到创建者中?在这种情况下,我可能会提供一个注册方法,而不是让每个类访问映射本身。使用单个注册方法使线程安全会容易得多,这是有意义的。但是,有没有保证在调用基类的Parse方法之前,所有的子类都会执行它们的静态构造函数?没有,我认为你不能保证。唯一的保证是静态构造函数在第一次使用该类之前就存在。