Я думаю, что вы недалеко от решения.В этом случае я бы ввел фабрику в ваш AccountService
, который взял бы на себя ответственность if..then..else
.Тогда завод мог бы использовать одно из многих возможных решений.
Одно изменение, которое я бы сразу сделал, заключается в том, что я заставил бы ваш AccountService реализовать интерфейс, который должен облегчить внедрение позже.Предполагая, что вы используете какой-то контейнер IOC, вам не нужно слишком беспокоиться о зависимостях, потому что вы позволяете контейнеру обрабатывать все это.
Вот части, которые у вас уже были, с некоторыми незначительными настройками:
public class Account
{
//some account information and behavior
}
public interface ISecurityContext
{
}
public class UsernamePasswordContext : ISecurityContext
{
public string Username { get; set; }
public string Password { get; set; }
}
public class SessionContext : ISecurityContext
{
public string SessionId { get; set; }
}
Вот ваш сервис аккаунта и его реализация:
public interface IAccountService
{
Account GetAccountFromSecurityContext(ISecurityContext securityContext);
}
public class AccountService : IAccountService
{
readonly IAccountFactory _accountFactory;
public AccountService(IAccountFactory accountFactory)
{
_accountFactory = accountFactory;
}
public Account GetAccountFromSecurityContext(ISecurityContext securityContext)
{
Account account = _accountFactory.Create(securityContext);
return account;
}
}
Итак, вы можете видеть здесь, что я ввел IAccountFactory
, который будет обрабатывать фактическое создание (поиск, что угодно) объекта Account
.На данный момент все, что нас волнует, - это то, что учетная запись создается / извлекается ... нас не волнует, как это сделать.
Существует несколько способов реализации подобной фабрики.Один из способов - использовать тип стратегии, где у вас есть список виджетов, которые знают, как разрешить учетную запись.Затем вы просто выбираете соответствующий виджет (стратегию) и выполняете его.Нечто похожее на это может быть фабрика, которая использует IOC или локатор службы для разрешения типа, который был ранее зарегистрирован в конфигурации приложения.
В качестве примера, вот одна из возможных реализаций IAccountFactory
с использованиемCommonServiceLocator
:
public interface IAccountFactory
{
Account Create(ISecurityContext securityContext);
}
public class ServiceLocatorAccountFactory : IAccountFactory
{
readonly IServiceLocator _serviceLocator;
public ServiceLocatorAccountFactory(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
}
public Account Create(ISecurityContext securityContext)
{
var resolverType = typeof (IAccountResolver<>).MakeGenericType(securityContext.GetType());
dynamic resolver = _serviceLocator.GetInstance(resolverType);
return resolver.Resolve(securityContext);
}
}
Моя фабрика выходит из контекста локатора службы и получает любой распознаватель, соответствующий нашему контексту безопасности.Вот несколько примеров возможных распознавателей:
public interface IAccountResolver<in TSecurityContext> where TSecurityContext : ISecurityContext
{
Account Resolve(TSecurityContext securityContext);
}
public class UsernamePasswordAccountResolver : IAccountResolver<UsernamePasswordContext>
{
readonly IRepository _repository;
public UsernamePasswordAccountResolver(IRepository repository)
{
_repository = repository;
}
public Account Resolve(UsernamePasswordContext securityContext)
{
var account = _repository.GetByUsernameAndPassword(securityContext.Username,
securityContext.Password);
return account;
}
}
public class SessionAccountResolver : IAccountResolver<SessionContext>
{
public Account Resolve(SessionContext securityContext)
{
//get the account using the session information
return someAccount;
}
}
Осталось только зарегистрировать распознаватели в контейнере IOC, чтобы их можно было найти, когда локатор служб пытается разрешить их на фабрике.