Итак, я подумал о нескольких способах подойти к этому, но я думаю, что самый простой способ - это просто ввести несколько интерфейсов токенов. Интерфейс токена - это интерфейс, который не добавляет никаких свойств или функциональности. Например:
interface IBusinessLogic
{
void DoBusinessLogic();
}
interface ITypeABusinessLogic : IBusinessLogic { }
interface ITypeBBusinessLogic : IBusinessLogic { }
interface IApplicationLogic
{
void DoApplicationLogic();
}
interface ITypeAApplicationLogic : IApplicationLogic { }
interface ITypeBApplicationLogic : IApplicationLogic { }
Затем мы настраиваем классы для реализации соответствующего интерфейса токена:
class TypeABusinessLogic : ITypeABusinessLogic
{
public virtual void DoBusinessLogic()
{
Console.WriteLine("Do Business Logic for Type A");
}
}
class TypeBBusinessLogic : ITypeBBusinessLogic
{
public virtual void DoBusinessLogic()
{
Console.WriteLine("Do Business Logic for Type B");
}
}
class TypeAApplicationLogic : ITypeAApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("Do Application Logic for Type A");
}
}
class TypeBApplicationLogic : ITypeBApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("Do Application Logic for Type B");
}
}
Аналогичным образом мы можем создавать фиктивные классы, реализуя соответствующий интерфейс токена:
class MockTypeABusinessLogic : ITypeABusinessLogic
{
public void DoBusinessLogic()
{
Console.WriteLine("[Mock] Do Business Logic for Type A");
}
}
class MockTypeBBusinessLogic : ITypeBBusinessLogic
{
public void DoBusinessLogic()
{
Console.WriteLine("[Mock] Do Business Logic for Type B");
}
}
class MockTypeAApplicationLogic : ITypeAApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("[Mock] Do Application Logic for Type A");
}
}
class MockTypeBApplicationLogic : ITypeBApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("[Mock] Do Application Logic for Type B");
}
}
Я также изменил интерфейс IStrategy, чтобы немного упростить внедрение с Unity, дав каждой стратегии свойство Name (вам не нужно это делать):
interface IStrategy
{
string Name { get; }
void DoWork();
}
abstract class StrategyBase : IStrategy
{
private IBusinessLogic _businessLogic;
private IApplicationLogic _applicationLogic;
public string Name { get; private set; }
protected StrategyBase(String name, IBusinessLogic businessLogic, IApplicationLogic applicationLogic)
{
this.Name = name;
_businessLogic = businessLogic;
_applicationLogic = applicationLogic;
}
public void DoWork()
{
_businessLogic.DoBusinessLogic();
_applicationLogic.DoApplicationLogic();
}
}
class TypeAStrategy : StrategyBase
{
public TypeAStrategy(String name, ITypeABusinessLogic businessLogic, ITypeAApplicationLogic applicationLogic) : base(name, businessLogic, applicationLogic)
{ }
}
class TypeBStrategy : StrategyBase
{
public TypeBStrategy(String name, ITypeBBusinessLogic businessLogic, ITypeBApplicationLogic applicationLogic) : base(name, businessLogic, applicationLogic)
{ }
}
Используя Unity, я написал следующую программу для проверки регистраций:
class Context
{
private Dictionary<string, IStrategy> _strategyFactory = new Dictionary<string, IStrategy>();
public Context(IStrategy[] strategies)
{
foreach (var s in strategies)
{
_strategyFactory.Add(s.Name, s);
}
}
public void Run()
{
string userInput = "TypeA";
IStrategy strategy = _strategyFactory[userInput];
strategy.DoWork();
userInput = "TypeB";
strategy = _strategyFactory[userInput];
strategy.DoWork();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Mock DI Example: ");
UnityContainer ioc = new UnityContainer();
ioc.RegisterType<ITypeABusinessLogic, MockTypeABusinessLogic>();
ioc.RegisterType<ITypeAApplicationLogic, MockTypeAApplicationLogic>();
ioc.RegisterType<ITypeBBusinessLogic, MockTypeBBusinessLogic>();
ioc.RegisterType<ITypeBApplicationLogic, MockTypeBApplicationLogic>();
ioc.RegisterType<IStrategy, TypeAStrategy>("TypeA", new InjectionConstructor("TypeA", typeof(ITypeABusinessLogic), typeof(ITypeAApplicationLogic)));
ioc.RegisterType<IStrategy, TypeBStrategy>("TypeB", new InjectionConstructor("TypeB", typeof(ITypeBBusinessLogic), typeof(ITypeBApplicationLogic)));
Context c = ioc.Resolve<Context>();
c.Run();
Console.WriteLine("\nUnmocked DI Example: ");
ioc = new UnityContainer();
ioc.RegisterType<ITypeABusinessLogic, TypeABusinessLogic>();
ioc.RegisterType<ITypeAApplicationLogic, TypeAApplicationLogic>();
ioc.RegisterType<ITypeBBusinessLogic, TypeBBusinessLogic>();
ioc.RegisterType<ITypeBApplicationLogic, TypeBApplicationLogic>();
ioc.RegisterType<IStrategy, TypeAStrategy>("TypeA", new InjectionConstructor("TypeA", typeof(ITypeABusinessLogic), typeof(ITypeAApplicationLogic)));
ioc.RegisterType<IStrategy, TypeBStrategy>("TypeB", new InjectionConstructor("TypeB", typeof(ITypeBBusinessLogic), typeof(ITypeBApplicationLogic)));
c = ioc.Resolve<Context>();
c.Run();
Console.WriteLine("\nPress enter to exit...");
Console.ReadLine();
}
И вот мой вывод:
Mock DI Пример:
[Mock] Логика Do Business для типа A
[Mock] Do Application Logic для типа A
[Макет] Do Business Logic для типа B
[Mock] Do Application Logic для типа B
Unmocked DI Пример:
Логика ведения бизнеса для типа А
Прикладная логика для типа A
Do Business Logic для типа B
Прикладная логика для типа B
Нажмите Enter, чтобы выйти ...
Это не единственный способ решить проблему, но я думаю, что это наиболее точно соответствует тому, как вы структурировали свой код в OP. Надеюсь, это поможет:)
РЕДАКТИРОВАТЬ: Вот одна из альтернатив, выше, я думаю, вы должны рассмотреть. Это значительно сократит иерархию вашего объекта и интерфейса. ПРИМЕЧАНИЕ: вам нужно сделать класс StrategyBase не абстрактным и предоставить конструктор открытым.
Console.WriteLine("\nAlternative DI Example: ");
ioc = new UnityContainer();
ioc.RegisterType<IBusinessLogic, TypeABusinessLogic>("TypeA");
ioc.RegisterType<IApplicationLogic, TypeAApplicationLogic>("TypeA");
ioc.RegisterType<IStrategy, StrategyBase>("TypeA", new InjectionConstructor("TypeA", new ResolvedParameter<IBusinessLogic>("TypeA"), new ResolvedParameter<IApplicationLogic>("TypeA") ));
ioc.RegisterType<IBusinessLogic, TypeBBusinessLogic>("TypeB");
ioc.RegisterType<IApplicationLogic, TypeBApplicationLogic>("TypeB");
ioc.RegisterType<IStrategy, StrategyBase>("TypeB", new InjectionConstructor("TypeB", new ResolvedParameter<IBusinessLogic>("TypeB"), new ResolvedParameter<IApplicationLogic>("TypeB")));
c = ioc.Resolve<Context>();
c.Run();
Поскольку ваши классы и интерфейсы токенов на самом деле не предоставляют вам никакой функциональности, они служат только для дифференциации конкретных реализаций. Но DI-контейнеры уже имеют простой способ сделать это: строки. В Unity вы можете использовать одну и ту же строку для разных типов, как указано выше. Вы можете использовать это, чтобы определить, какие конкретные реализации идут вместе. Это моя рекомендация:)