我们的应用程序中有一个非常常见的对象。在这种情况下,我们称之为Ball。球很好,但在某些配置中,它们的行为不同。它目前设置如下:
class Ball
{
private static readonly bool BallsCanExplode;
static Ball()
{
bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"],
out BallsCanExplode);
}
public Ball(){}
}
这在实践中完全正常。如果配置是球可以爆炸,它们会爆炸,如果不是,则不会爆炸。问题是它完全不可测试。我无法找到一种保持可测试性的好方法,并且仍然易于实例化。
最简单的解决方案是将球和配置分离:
class Ball
{
private readonly bool CanExplode;
public Ball(bool canExplode);
}
这个问题在于Ball类中曾经是一个孤立的依赖现在已经扩展到制作Ball的每一个类。如果这种依赖注入,那么爆炸球的知识必须到处注入。
BallFactory存在同样的问题。虽然每个班级都可以new Ball()
,但现在必须知道必须在任何地方注入的BallFactory。另一种选择是使用已经在应用程序中烘焙的服务定位器:
class Ball
{
private readonly bool CanExplode;
public Ball()
{
CanExplode = ServiceLocator.Get<IConfiguration>().Get("ballsCanExplode");
}
}
这仍然保持了球的配置依赖性,但允许注入测试配置。尽管使用了球,但在每次new Ball()
调用时找到服务似乎有点过分。
保持这种可测试性以及易于实例化的最佳方法是什么?
注意:应用程序中既有依赖注入框架又有服务定位器,它们都经常使用。
答案 0 :(得分:5)
实例化球的类应该接收BallFactory
作为依赖项。无论是否生成爆炸球或非爆炸球,BallFactory
都可以相应地配置为应用程序启动。
不要让BallFactory
读取应用程序配置文件来确定要生成哪种类型的球。这应该注入BallFactory
。
服务定位器是一种反模式。不要使用它们。
答案 1 :(得分:2)
我会像你的ServiceLocator一样使用 来设置 static DefaultBallsCanExplode,然后可能有一个重载的构造函数,可以将ballCanExplode bool作为一个选项。
保持简单!
答案 2 :(得分:2)
据我所知,你应用中的所有球总是一样。它们是爆炸还是不爆炸,由配置开关决定。你可以做的是在你的DI框架中配置它。根据框架,应用程序根目录中的连线可能如下所示::
bool ballsCanExplode =
bool.Parse(ConfigurationManager.AppSettings["ballsCanExplode"]);
container.Register<Ball>(() => new Ball(ballsCanExplode));
执行此操作时,您可以使用服务定位器模式来获取球的新实例,如您已经习惯的那样:
ServiceLocator.Get<Ball>();
但更好的方法是让DI框架在其他类型的构造函数中注入Ball
依赖项(更容易进行测试)。
答案 3 :(得分:0)
我投票支持配置服务路径。对于典型的服务定位器实现,开销不应该很高,如果以后需要,可以缓存配置服务。更好的是,使用依赖注入框架,您不需要显式定位服务。
答案 4 :(得分:0)
这样的事情怎么样:
internal interface IBallConfigurer
{
bool CanExplode { get; }
}
internal class BallConfigurer : IBallConfigurer
{
public bool CanExplode
{
get
{
bool BallsCanExplode;
bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"],
out BallsCanExplode);
return BallsCanExplode;
}
}
}
public class Ball
{
private bool canExplode;
public Ball()
:this(new BallConfigurer())
{
}
internal Ball(IBallConfigurer ballConfigurer)
{
this.canExplode = ballConfigurer.CanExplode;
}
}
这样,您可以使球类内部对单元测试组件可见,并注入自定义球形配置器。