如何根据最终用户配置值解析类型?

时间:2010-02-01 21:41:48

标签: dependency-injection unity-container

我有一个有多个实现的接口(称之为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

这似乎不对,因为我的班级必须知道容器。但我无法想出另一种方式。

在没有课程了解容器的情况下,是否还有其他方法可以允许最终用户选择?

1 个答案:

答案 0 :(得分:9)

定义和实施Abstract Factory是此类问题的标准解决方案。如果你原谅我使用C#,你可以像这样定义一个IAcmeServiceFactory接口:

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中注册Factory。在任何需要基于名称的IAcmeService的地方,您需要依赖IAcmeServiceFactory而不是IAcmeService本身。