我有一个具有多个实现的接口(interface)(称为IAcmeService)。
FileSystemAcmeService
DatabaseAcmeService
NetworkAcmeService
最终用户需要能够选择将要使用的实现并保存该选择。
目前,我正在配置IOC容器(Unity)以使用名称注册所有已知的实现。
container.RegisterType(of IAcmeService, FileSystemAcmeService)("FileSystemAcmeService")
container.RegisterType(of IAcmeService, DatabaseAcmeService)("DatabaseAcmeService")
container.RegisterType(of IAcmeService, NetworkAcmeService)("NetworkAcmeService")
为了允许用户保存其选择,我有app.config配置节文件,该文件存储了要使用的所选服务名称。
为了解决所选的实现,我正在使用服务的类的Initialize方法中进行手动“解决”。
Private _service as IAcmeService
Public Sub Initialize()
_service = container.Resolve(of IAcmeService)(_config.AcmeServiceName)
End Sub
这似乎不对,因为我的类(class)必须了解容器。但是我想不出另一种方法。
在类不知道容器的情况下,还有其他方法可以允许最终用户选择吗?
最佳答案
定义和实现Abstract Factory是此类问题的标准解决方案。如果您可以不使用C#,则可以定义一个IAcmeServiceFactory接口(interface),如下所示:
public interface IAcmeServiceFactory
{
IAcmeService Create(string serviceName);
}
现在,您可以编写一个像这样的具体实现:
public class AcmeServiceFactory : IAcmeServiceFactory
{
private readonly IAcmeService fsService;
private readonly IAcmeService dbService;
private readonly IAcmeService nwService;
public AcmeServiceFactory(IAcmeService fsService,
IAcmeService dbService, IAcmeService nwService)
{
if (fsService == null)
{
throw new ArgumentNullException("fsService");
}
if (dbService == null)
{
throw new ArgumentNullException("dbService");
}
if (nwService == null)
{
throw new ArgumentNullException("nwService");
}
this.fsService = fsService;
this.dbService = dbService;
this.nwService = nwService;
}
public IAcmeService Create(string serviceName)
{
switch case serviceName
{
case "fs":
return this.fsService;
case "db":
return this.dbService;
case "nw":
return this.nwService;
case default:
throw new ArgumentException("serviceName");
}
}
}
如果希望能够创建任意数量的IAcmeService实例,可以使其更通用,但我将其留给读者练习:)
这将要求您也向Unity注册工厂。在需要基于名称的IAcmeService的任何地方,都需要依赖IAcmeServiceFactory而不是IAcmeService本身。
关于dependency-injection - 如何根据最终用户配置值解析类型?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2180276/