我有一个具有多个实现的接口(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/

10-09 22:15