常用对象具有配置依赖性

时间:2010-11-03 15:29:47

标签: c# design-patterns configuration dependency-injection

我们的应用程序中有一个非常常见的对象。在这种情况下,我们称之为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()调用时找到服务似乎有点过分。

保持这种可测试性以及易于实例化的最佳方法是什么?

注意:应用程序中既有依赖注入框架又有服务定位器,它们都经常使用。

5 个答案:

答案 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;
    }
}

这样,您可以使球类内部对单元测试组件可见,并注入自定义球形配置器。