Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xcode/7.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
Dependency injection 依赖注入&x2B;环境上下文+;服务定位器_Dependency Injection_Service Locator_Cross Cutting Concerns - Fatal编程技术网

Dependency injection 依赖注入&x2B;环境上下文+;服务定位器

Dependency injection 依赖注入&x2B;环境上下文+;服务定位器,dependency-injection,service-locator,cross-cutting-concerns,Dependency Injection,Service Locator,Cross Cutting Concerns,最近我读了很多关于应用程序设计模式的东西:关于DI、SL反模式、AOP等等。原因是——我想达成一个设计折衷方案:松散耦合、干净且易于使用。DI看起来几乎像是一个解决方案,除了一个问题:横切和可选依赖性导致构造函数或属性污染。因此,我提出了自己的解决方案,我想知道您对此有何看法 Mark Seemann(DI书的作者,著名的“SL是反模式的”陈述)在他的书中提到了一种称为环境语境的模式。尽管他说他不太喜欢它,但这个模式仍然很有趣:它就像旧的优秀的singleton,只是它的作用域是有限的,并且提供

最近我读了很多关于应用程序设计模式的东西:关于DI、SL反模式、AOP等等。原因是——我想达成一个设计折衷方案:松散耦合、干净且易于使用。DI看起来几乎像是一个解决方案,除了一个问题:横切和可选依赖性导致构造函数或属性污染。因此,我提出了自己的解决方案,我想知道您对此有何看法

Mark Seemann(DI书的作者,著名的“SL是反模式的”陈述)在他的书中提到了一种称为环境语境的模式。尽管他说他不太喜欢它,但这个模式仍然很有趣:它就像旧的优秀的singleton,只是它的作用域是有限的,并且提供了默认值,所以我们不必检查null。它有一个缺陷——它没有,也不知道它的范围和如何处理自己

那么,为什么不在这里应用服务定位器呢?它可以解决环境上下文对象的作用域和处理问题。在你说它是反模式之前:它是当你隐藏合同时。但在我们的例子中,我们隐藏了可选合同,所以在我看来,这并不坏

这里有一些代码来说明我的意思:

public interface ILogger
{
    void Log(String text);
}

public interface ISomeRepository
{
    // skipped
}


public class NullLogger : ILogger
{
    #region ILogger Members

    public void Log(string text)
    {
        // do nothing
    }

    #endregion
}

public class LoggerContext
{
    public static ILogger Current
    {
        get
        {
            if(ServiceLocator.Current == null)
            {
                return new NullLogger();
            }
            var instance = ServiceLocator.Current.GetInstance<ILogger>();
            if (instance == null)
            {
                instance = new NullLogger();
            }
            return instance;
        }
    }
}

public class SomeService(ISomeRepository repository)
{
    public void DoSomething()
    {
        LoggerContext.Current.Log("Log something");
    }
}
公共接口ILogger
{
无效日志(字符串文本);
}
公共接口异构存储
{
//跳过
}
公共类NullLogger:ILogger
{
#区域ILogger成员
公共作废日志(字符串文本)
{
//无所事事
}
#端区
}
公共类LoggerContext
{
公共静态ILogger电流
{
得到
{
if(ServiceLocator.Current==null)
{
返回新的NullLogger();
}
var instance=ServiceLocator.Current.GetInstance();
if(实例==null)
{
实例=新的NullLogger();
}
返回实例;
}
}
}
公共类服务(异构存储库)
{
公共无效剂量测定法()
{
LoggerContext.Current.Log(“记录某物”);
}
}
编辑:我意识到不问具体问题与堆栈溢出设计相冲突。因此,我会在一篇文章中给出一个最好的答案,描述为什么这种设计不好或更好,并给出更好的解决方案(或者添加?)。但不要建议使用AOP,这很好,但当您真的想在代码中执行某些操作时,它不是一个解决方案


编辑2:我为ServiceLocator添加了一个检查。当前为空。这就是我的代码要做的:在没有配置SL的情况下使用默认设置。

您所建议的环境上下文的一个问题是,它使测试更加困难。出于以下几个原因:

  • 运行单元测试时,必须始终在“ServiceLocator.Current”中注册有效实例。但不仅如此,它还必须注册到有效的
    ILogger
  • 当您需要在测试中使用假记录器(而不是简单的 NullLogger),您必须配置容器,因为 没有办法连接到这一点,但是由于容器是一个单例,所有其他测试都将使用相同的记录器
  • 创建一个在单元测试并行运行时工作的解决方案(默认情况下MSTest就是这样)将是非常重要的(也是浪费时间)
  • 所有这些问题都可以通过简单地将
    ILogger
    实例注入到需要它的服务中来解决,而不是使用环境上下文

    如果系统中有许多类依赖于
    ILogger
    抽象,那么您应该认真地问问自己


    还请注意。

    您可以使用手工制作的装饰器或某种拦截(例如,或)添加横切关注点


    因此,您根本不必将
    ILogger
    注入核心业务类。

    您的问题中缺少的是一个示例,您清楚地表明需要使用
    LoggerContext.Current
    而不是在代码中注入
    ILogger
    。根据我的经验,如果您需要在整个代码中注入许多
    ILogger
    依赖项,那么您要么记录得太多(而不是抛出异常),要么没有遵守SRP(实际上您确实需要AOP)。请看以下答案:.1。史蒂文,我忘了检查ServiceLocator。当前为空,谢谢你指出这一点。通过此检查,测试不会被迫初始化SL.2。为什么我首先要测试像ILogger这样的东西?但如果我愿意,这仍然是可行的。3.我同意-如果你想测试它,这是一个问题。但再一次,这是可行的。最后,更重要的是:轻松编写真正的代码还是轻松编写测试。有很多研究证明,如果不能够测试代码,就无法编写高质量的代码。因此,能够轻松编写测试对于编写高质量的应用程序至关重要。@DmitryGlobets:让我来扭转这一局面。为什么不想在类中测试ILogger的用法?既然您已经编写了这段代码,那么这一定是有价值的业务逻辑,您应该想知道这段代码是否正确。如果这段代码不是很有价值,你为什么不先写呢?也许这一行是一个横切关注点(AOP),而不是重要的业务逻辑。在这种情况下,您不应该使您的业务逻辑复杂化,您应该将此逻辑提取到装饰程序或某种类型的程序中。关于第3点。看起来MSTest默认以串行方式运行测试()是的,我到处都读到了相同的建议,但我觉得它不对。有时,日志应该写入某个“if”分支中的方法内部。拦截并不能解决这个问题。ILogger只是一个例子,但也可能有其他接口具有读取某些值的方法。@DmitryGlobets:如果您认为日志记录是类的一个重要方面,那么请明确这一点,并使用构造函数注入将其注入。如果它用于跟踪输入值和日志异常(我处理日志和