可扩展的工厂设计模式

本文关键字:设计模式 工厂 可扩展的 | 更新日期: 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,并且不需要更改工厂类。

http://www.dofactory.com/net/prototype-design-pattern

你可以这样写:(为清晰起见,省略了错误处理)

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)