Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有配置依赖关系的常用对象_C#_Design Patterns_Configuration_Dependency Injection - Fatal编程技术网

C# 具有配置依赖关系的常用对象

C# 具有配置依赖关系的常用对象,c#,design-patterns,configuration,dependency-injection,C#,Design Patterns,Configuration,Dependency Injection,我们的应用程序中有一个非常常见的对象。在这种情况下,我们称之为球。球工作正常,但在某些配置中,它们的行为不同。目前的设置如下: class Ball { private static readonly bool BallsCanExplode; static Ball() { bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"], out BallsCa

我们的应用程序中有一个非常常见的对象。在这种情况下,我们称之为球。球工作正常,但在某些配置中,它们的行为不同。目前的设置如下:

class Ball
{
    private static readonly bool BallsCanExplode;
    static Ball()
    {
        bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"], 
            out BallsCanExplode);
    }
    public Ball(){}
}
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;
    }
}
这在实践中效果非常好。如果配置为球可以爆炸,则它们会爆炸,如果不爆炸,则不会爆炸。问题是它是完全不可测试的。我还没有找到一个好方法来保持它的可测试性,并且仍然易于实例化

最简单的解决方案是将球与配置解耦:

class Ball
{
    private readonly bool CanExplode;
    public Ball(bool canExplode);
}
这样做的问题是,Ball类中曾经是一个孤立的依赖项的内容现在扩展到了生成Ball的每个类。如果这种依赖性被注入,那么爆炸球的知识就必须被注入到任何地方

球厂也存在同样的问题。虽然每个类都可以使用
newball()
,但它现在必须知道必须到处注入的BallFactory。另一个选项是使用已烘焙到应用程序中的服务定位器:

class Ball
{
    private readonly bool CanExplode;
    public Ball()
    {
        CanExplode = ServiceLocator.Get<IConfiguration>().Get("ballsCanExplode");
    }
}
班级舞会
{
私有只读bool CanExplode;
公共舞会
{
CanExplode=ServiceLocator.Get().Get(“ballsCanExplode”);
}
}
这仍然将配置依赖性保留在球中,但允许注入测试配置。不过,Ball的使用太多,在每次
newball()
调用中查找服务似乎有些过分

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


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

实例化balls的类应该作为依赖项接收一个
BallFactory
。无论是否生产爆炸球或非爆炸球,
球厂
都可以相应地配置为应用程序启动

不要让
BallFactory
读取应用程序配置文件以确定要生产的球的类型。应将其注入
球厂


服务定位器是一种反模式。不要使用它们。

我投票赞成配置服务路径。对于典型的服务定位器实现来说,开销不应该太高,如果以后需要,可以缓存配置服务。更好的是,使用依赖项注入框架,您不需要显式地定位服务。

类似这样的情况如何:

class Ball
{
    private static readonly bool BallsCanExplode;
    static Ball()
    {
        bool.TryParse(ConfigurationManager.AppSettings["ballsCanExplode"], 
            out BallsCanExplode);
    }
    public Ball(){}
}
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;
    }
}
通过这种方式,您可以使ball类内部构件对您的单元测试程序集可见,并注入一个自定义ballconfigurer。

我将使用类似于ServiceLocator的东西来设置一个静态的DefaultBallsCanExplode,然后可能有一个重载构造函数,可以将ballsCanExplode bool作为一个选项


保持简单

正如我正确理解的,应用程序中的所有球的行为都是一样的。它们要么爆炸,要么不爆炸,由配置开关决定。您可以做的是在DI框架中配置它。根据框架的不同,应用程序根目录中的连接可能如下所示:

bool ballsCanExplode =
    bool.Parse(ConfigurationManager.AppSettings["ballsCanExplode"]);

container.Register<Ball>(() => new Ball(ballsCanExplode)); 
bool-ballscan爆炸=
解析(ConfigurationManager.AppSettings[“ballsCanExplode”]);
容器。寄存器(()=>新球(ballsCanExplode));
执行此操作时,您可以使用服务定位器模式获取球的新实例,就像您已经使用的那样:

ServiceLocator.Get<Ball>();
ServiceLocator.Get();

但是最好让DI框架在其他类型的构造函数中注入
Ball
依赖项(更容易测试)。

像Ninject这样的D.I.库可以帮助您吗?您似乎知道依赖项注入选项,但不想遵循它们。因此,您最好的选择是添加一个用于测试的构造函数:如果您没有其他依赖项注入方法,那么publicball(boolcanexplode)服务定位器可以是一种干净的方法来完成这项工作。工厂在许多情况下都可能是杀伤力过大的,尽管它在这种特殊情况下可能是合适的(在这种情况下,您需要创建同一类的多个实例,并在每种情况下解析一个设置)。服务定位器已经在应用程序中无处不在,因此它已经存在,无论它是否是反模式。有些东西使用它,有些东西使用依赖项注入。@Snea:嗯,我认为你应该停止在服务定位器上添加更多依赖项。这基本上是我尝试的第一件事,但使用内置BallConfigurer似乎无法实现将Ball与AppSettings类解耦的目的。嗯,您说过希望球易于实例化,但希望它可测试。这给了你一切。您从未说过ball与AppSetting类的耦合过于紧密,您只是希望它足够解耦以进行单元测试。这使您知道,+1配置是应用程序初始化的一部分,而不是其运行时。