Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/286.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# 针对横切问题,建造师注入(DI)与静态工厂?_C#_Design Patterns_Dependency Injection_Factory_Cross Cutting Concerns - Fatal编程技术网

C# 针对横切问题,建造师注入(DI)与静态工厂?

C# 针对横切问题,建造师注入(DI)与静态工厂?,c#,design-patterns,dependency-injection,factory,cross-cutting-concerns,C#,Design Patterns,Dependency Injection,Factory,Cross Cutting Concerns,在大多数任意应用程序中,需要在所有可用层之间解决许多交叉问题,例如日志记录、消息总线、配置。我注意到,在某些类中,如果使用IoC注入模块,它们往往会完全破坏构造函数 public class MyService : IService { public MyService(ILogger logger, IAppSettings settings, IEventBus eventBus...) { } } 对于构造器过度注入的常见情况,我倾向于将关注点折射到紧密属于一

在大多数任意应用程序中,需要在所有可用层之间解决许多交叉问题,例如日志记录、消息总线、配置。我注意到,在某些类中,如果使用IoC注入模块,它们往往会完全破坏构造函数

public class MyService : IService 
{
    public MyService(ILogger logger, IAppSettings settings, IEventBus eventBus...)
    {

    }
}
对于构造器过度注入的常见情况,我倾向于将关注点折射到紧密属于一起的构建块中,以便在类中获得较少的依赖项。然而,这在交叉概念中是不可能的

在日志框架中,静态工厂/服务似乎非常流行,例如

// Application root
MyLoggerService.SetFactory(log4NetFactory);

// Somewhere
MyLoggerService.GetLogger("name") // returns Log4NetLogger created by Log4NetFactory.
我的问题是:对于各种交叉的东西,这种方法是好的吗?如果代码最终看起来像这样,有哪些缺点:

public class MyService : IService
{

    private readonly IReallyNeedThat _dependency;

    public MyService(IReallyNeedThat dependency)
    {
        _dependency = dependency;
    }

    private readonly ILogger _logger = LoggerService.GetLogger("MyService");
    private readonly IEventBus _eventBus = EventBusService.GetEventBus();
    private readonly IConfiguration _configuration = ConfigurationService.GetConfiguration(Level.Roaming)
    private readonly IExceptionHandler _exceptionHandler = ExceptionPolicy.GetHandler();
    private readonly ITracer _tracer = TraceManager.GetDebugTracer();
}

如果您更了解TDD,您可以很容易地猜测哪种方法更好

通过依赖项注入,您的代码变得更易于测试。您可以通过一些模拟框架注入依赖项并创建单元测试,而无需太多麻烦

但是在静态工厂的情况下,因为工厂类(硬)连接到类中,而单元测试没有办法从类外部注入它们

DI相对于静态工厂的好处-

  • 并发开发-想象一下您正在使用的日志服务,它是由其他人构建的,您将要对代码进行单元测试(您不关心日志服务的单元测试,因为您假设,在使用它时应该对它进行单元测试)。您可以使用DI,使用模拟对象注入依赖项,然后完成

  • 速度-在单元测试您的类时,您肯定不希望它们花费很长时间(这样,您就可以在主类的每一次更改中都有一次休息时间;)。您肯定希望单元测试在眨眼之间运行并报告任何错误。依赖于外部资源(例如网络/DB、文件系统)的静态工厂将需要时间。您最好使用DI,使用模拟对象,然后完成

  • 可测试性-DI有助于将客户机与其依赖项隔离开来(促进接口的使用),从而提高可测试性(通过使用模拟)


  • 将依赖项移出构造函数并不能解决问题,因为您没有降低类的依赖项数量,并且仍然违反和更改的可能性很大,这导致您的代码很难测试、更改和维护

    通常情况下,一个好的解决方案是将这些横切关注点从组件中拉出,并将它们放入专门为该横切关注点定制的组件中,然后该组件将原始组件包装起来。换句话说:创造

    这可能会迫使您更改类的设计,因为当您没有通用抽象来定义相关服务集时,您必须为每个抽象定义一个装饰器,这将导致大量代码重复,这在几乎所有情况下都是不好的


    因此,相反,对你的系统进行建模,你会处于一个更好的位置。您可以使用一个通用的装饰器来装饰业务逻辑的每一部分,您只需定义一次,然后就可以到处重用。这使您的系统保持干净,但仍然非常灵活。

    虽然没有直接关系,但很有趣:.Read和@MarcelN。的确依我看,这是解决依赖项/参数过多问题的正确方法。@YuvalItzchakov是很好的例子,但是,正如您在第二个例子中所看到的,作者将几个域关注点捆绑到了一个facade中,该facade执行特定的域相关任务。我认为这在交叉切割方面没有意义。。我的意思是,在一个类中我需要一个记录器、一个异常处理程序和一个跟踪程序,在另一个类中我需要一个记录器、一个跟踪程序和一个事件总线。那么外观会是什么样子呢?ICOServicesWithLogger、ICOServicesWithOutEventBus、ICOServicesForExceptions?创建多个外观,并根据需要将适当的外观注入类中。我要做的是将记录器、异常处理程序和跟踪器封装到一个facade服务中(仅仅因为它们的工作线似乎相似),然后将该facade注入另一个facade,其中还包括事件总线。我想补充一点,您通常不希望(需要)忽略交叉关注点。因此,在OP的示例中,事件总线可能是您不希望是静态的。我想说,即使你没有做TDD,情况也是如此,但是用这些术语来思考它,可以更容易地看到什么可以/应该被嘲笑(从而被注入),什么不应该被嘲笑。