Как разрешить тип на основе значения конфигурации конечного пользователя?
У меня есть интерфейс (назовите его 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 ответ
Определение и реализация и Абстрактная Фабрика - стандартное решение этого типа проблемы. Если вы простите мое использование 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.