Как разрешить тип на основе значения конфигурации конечного пользователя? - PullRequest
10 голосов
/ 02 февраля 2010

У меня есть интерфейс (назовите его 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 Ответ

9 голосов
/ 02 февраля 2010

Определение и реализация и Абстрактная фабрика является стандартным решением проблемы такого типа. Если вы простите мое использование 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. В любом месте, где вам нужен IAcmeService на основе имени, вы берете зависимость от IAcmeServiceFactory вместо самого IAcmeService.

...