可扩展的工厂设计模式
本文关键字:设计模式 工厂 可扩展的 | 更新日期: 2023-09-27 18:14:00
以下是我的导入工厂类。如果我想添加新的ImportTypes,那么只需在ImportTypes枚举中添加新类型,并在工厂类中添加case,它就可以正常工作了。
我的问题是如何使它更独立,更容易扩展。假设一个用户想要添加一个新的ImportType,而不是修改代码,他编写了自己的DLL并实现了接口…有什么好的建议吗?
导入类型enum ImportTypes
{
DefaultImport,
C2CImport
}
<<p> 导入接口/strong> public interface IImportService
{
void Import(Argument arguments, ImportDefinition config);
}
<<p> 进口工厂/strong> class ImportFactory
{
public static IImportService GetService(ImportTypes type)
{
switch (type)
{
case ImportTypes.DefaultImport:
return new DefaultImportService();
case ImportTypes.C2CImport:
return new C2CImportService();
}
return null;
}
}
IImportService docImportService = ImportFactory.GetService(ImportTypes.DefaultImport);
要实现这一点,您需要支持在运行时注册新服务。因此,您不能使用enum
,因为事先不知道这些服务;它们需要通过eg字符串来引用:
class ImportServiceHandler
{
private readonly Dictionary<string, Type> _importServices =
new Dictionary<string, Type>();
public ImportServiceHandler()
{
RegisterService("DefaultImport", typeof(DefaultImportService));
RegisterService("C2CImport", typeof(C2CImportService));
}
public void RegisterService(string name, Type serviceType)
{
if (!serviceType.IsAssignableFrom(typeof(IImportService)))
{
throw new ArgumentException("Type specified doesn't implement IImportService", nameof(serviceType));
}
_importServices.Add(name, serviceType);
}
public IImportService GetService(string name)
{
if (_importServices.ContainsKey(name))
{
return (IImportService)Activator.CreateInstance(_importServices[name]);
}
return null;
}
}
有一种方法…
在switch语句中,"default"将处理任何无法识别的类型。该类型将在程序的配置中查找。用户必须将其类型和路径添加到实现该类型的程序集。他们需要实现你的接口。默认情况将加载程序集并实例化对象并将其返回给您。
你可以使用AppDomain.CurrentDomain.Load()来加载程序集。您可以使用{Assembly}. createinstance()实例化您的对象。
class ImportFactory
{
public static IImportService GetService(ImportTypes type)
{
switch (type)
{
case ImportTypes.DefaultImport:
return new DefaultImportService();
case ImportTypes.C2CImport:
return new C2CImportService();
default:
return CustomImportService(type);
}
return null;
}
}
CustomImportService应该是这样的…
IImportService CustomImportService(ImportTypes type)
{
var assmBytes = GetAssemblyIDByType(type); // you would implement this based on your configuration approach
var assm = AppDomain.CurrentDomain.Load(assmBytes);
var obj = (IImportService)assm.CreateInstance("IImportService");
return obj;
}
当然,这只是一个开始,还需要努力。
您可以使用原型模式。
创建一个在运行时接收Import类型的类,并返回这些对象的克隆。
它将允许您在运行时加载ImportTypes,并且不需要更改工厂类。
你可以这样写:(为清晰起见,省略了错误处理)
public class Factory {
//store how different objects are created
private IDictionary<string, Func<IImportService>> mappings = new Dictionary<string, Func<IImportService>>()
public void Register(string key, Func<IImportService> expression)
{
mappings[key] = expression;
}
public IImportService GetService(string key)
{
return mappings[key]();
}
}
可以这样使用:
factory.Register("service", () => new MyCustomImportService());
如果在编译时知道想要的类型,则可以提供在签名中接受该类型的重写。即Register<MyCustomImportService>(...)
和GetService<MyCustomImportService>
。此时,如果您想要一些轻量级的IoC容器
我很想知道,当您使用enum来决定返回哪个实现时,如何创建可扩展的工厂模式。这意味着每次有人用新的实现逻辑构建新的DLL时,您都必须重构原始代码(这可能是一个苛刻的描述,因为您所做的只是添加一个新的enum元素),并重新构建它。这有点违背目的。
我强烈建议不要这样做。事实上,我看不出工厂模式如何被认为非常适合您想要做的事情,这更像是一个可插入的框架,它只知道它必须根据调用代码的请求返回IImportService
实现。
MEF
之类的?您可以这样定义依赖关系:
[Export("C2CImport", typeof(IImportService))]
public class C2CImportService : IImportService
除了[Import]
-ing的依赖到你想要的代码,你可以手动解析依赖,像这样:
myCompositionContainer.GetExports<IImportService>("C2CImport");
(https://msdn.microsoft.com/en-us/library/dd833299 (v = vs.110) . aspx)