【发布时间】:2018-11-08 21:41:07
【问题描述】:
我目前正在开发一个应用程序,该应用程序将根据用户输入采取不同的行动。所以我想到了策略模式。下面是我的实现:
我有一些业务逻辑:
interface IBusinessLogic
{
void DoBusinessLogic();
}
class TypeABusinessLogic : IBusinessLogic
{
public void DoBusinessLogic()
{
Console.WriteLine("Do Business Logic for Type A");
}
}
class TypeBBusinessLogic : IBusinessLogic
{
public void DoBusinessLogic()
{
Console.WriteLine("Do Business Logic for Type B");
}
}
还有一些应用逻辑:
interface IApplicationLogic
{
void DoApplicationLogic();
}
class TypeAApplicationLogic : IApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("Do Application Logic for Type A");
}
}
class TypeBApplicationLogic : IApplicationLogic
{
public void DoApplicationLogic()
{
Console.WriteLine("Do Application Logic for Type B");
}
}
现在,我的策略需要同时处理业务逻辑和应用程序逻辑
interface IStrategy
{
void DoWork();
}
abstract class StrategyBase : IStrategy
{
private IBusinessLogic _businessLogic;
private IApplicationLogic _applicationLogic;
protected StrategyBase(IBusinessLogic businessLogic, IApplicationLogic applicationLogic)
{
_businessLogic = businessLogic;
_applicationLogic = applicationLogic;
}
public void DoWork()
{
_businessLogic.DoBusinessLogic();
_applicationLogic.DoApplicationLogic();
}
}
class TypeAStrategy : IStrategy
{
public TypeAStrategy(TypeABussinessLogic businessLogic, TypeAApplicationLogic applicationLogic) : base(businessLogic, applicationLogic)
{}
}
class TypeBStrategy : IStrategy
{
public TypeBStrategy(TypeBBussinessLogic businessLogic, TypeBApplicationLogic applicationLogic) : base(businessLogic, applicationLogic)
{}
}
现在是我的上下文类
class Context
{
private Func<string, IStrategy> _strategyFactory;
public Context(Func<string, IStrategy> strategyFactory)
{
_strategyFactory = strategyFactory;
}
public void Run()
{
string userInput = GetUserInput(); //"TypeA" or "TypeB"
IStrategy strategy = _strategyFactory(userInput);
strategy.DoWork();
}
}
这是我的 DI 构建器代码:
var builder = new ContainerBuilder();
builder.RegisterType<TypeAStrategy>().As<IStrategy>().Keyed<IStrategy>("TypeA");
var builder = new ContainerBuilder();
builder.RegisterType<TypeBStrategy>().As<IStrategy>().Keyed<IStrategy>("TypeB");
builder.Register<Func<string, IStrategy>>( c =>
{
var componentContext = c.Resolve<IComponentContext>();
return (key) =>
{
IStrategy stategy = componentContext.ResolveKeyed<IStrategy >(key);
return stategy;
};
});
我在这里看到的问题是我的策略(TypeAStrategy,TypeBStrategy)直接依赖于具体类(TypeABusinessLogic,TypeAApplicationLogic,TypeBBusinessLogic,TypeBApplicationLogic),这不好。我无法在单元测试中模拟这些依赖项。
如果我让我的策略依赖于接口,我不知道如何实现 DI 容器来解决依赖关系(注意:我目前使用 Autofac,但我可以使用任何其他 DI 容器)
请指教。
【问题讨论】:
-
您不使用 DI 容器来解析来自用户的动态输入(任何注入都应在引导后完成)。你可以使用工厂。 DI 可用于注入可用的工厂,以及用于这些工厂的名称。例如,使用 Unity,您可以将工厂注册信息提取到配置文件中。然后,您可以修改从配置文件注册的工厂,而无需重新编译程序,并为每个工厂指定一个唯一的名称。您可以为每个单独的工厂提供您想要的任何名称,不需要是类名。
-
@Ryan:您能否提供有关如何为我的策略注册依赖项的更多详细信息。任何示例代码(即使在 Unity 中)都非常感谢。我添加了我的 ContainerBuilder 代码以使其更清晰。
-
如果其他人在我之前没有做到,今晚我会整理一个例子。无论如何,您当前的层次结构不需要每个策略的中间接口。您的策略并没有在功能或属性方面添加任何新内容,因此您的基本 IStrategy 接口就是模拟目的所需要的全部内容。
-
“每个策略的中间接口”是什么意思?您是在谈论 IBusinessLogic、IApplicationLogic 吗?我认为这是必要的,因为我想将不同的逻辑类别分成不同的类(TypeABusinessLogic 和 TypeAApplicationLogic 处理不同的逻辑)。它帮助我实现单一职责。
-
我的意思是你不需要 ITypeAStrategy 或 ITypeBStrategy 接口。这些策略不添加任何功能/属性,因此您可以仅使用基本 IStrategy 接口来模拟它们。
标签: c# design-patterns dependency-injection autofac ioc-container