【问题标题】:How to deal with cross-cutting concerns in a OO Application? Use Singleton? Dependency Injection? What?如何处理 OO 应用程序中的横切关注点?使用单例?依赖注入?什么?
【发布时间】:2011-04-01 05:56:19
【问题描述】:

假设我目前正在设计一个需要使用全局计时系统的应用程序(这是一个横切关注点)。我需要从我的应用程序中的任何位置访问来自该全局计时系统的数据,而且我无法看到“应用程序的这一部分需要它而另一部分不需要”。

我的问题是.. 我应该将其设计为一种环境上下文(在本例中为 Singleton),还是应该尝试设计其他方式来适应这种情况?

我当然认为让我的所有类都必须通过构造函数注入将这个全局计时类传递给它们是不正确的。很多时候,我不得不将引用真正传递到链中,直到某些类最终需要它。另一方面,从阅读的角度来看,它会让一切变得更加清晰(它让我的类的依赖关系变得清晰)。

人们通常如何处理这个问题?周围有没有其他技术可以帮助解决这个问题? AOP 可能吗?

PS:全球计时系统只是我从我正在阅读的一本书中得到的一个想法。日志系统将是此类问题的另一个很好的例子。

谢谢

【问题讨论】:

    标签: c# java oop singleton


    【解决方案1】:

    无论您是否将它实现为单例,我仍然建议您通过依赖注入传递它。我还建议您不要自己实现单例,而是使用 DI 框架为您提供任何一流的 IOC 库都会执行的单例上下文。

    【讨论】:

      【解决方案2】:

      我用 aop 做了什么:

      public abstract aspect AutoInjectAspect<T> { /** * Implemented by the concrete class to decide what value to return. * @param joinPoint * @return */ abstract public T getInjectedValue(JoinPoint joinPoint); protected pointcut autoInjectField() : get(@AutoInject T *); /** * override the "get" of the otherwise null value with the * value to be injected * @return the injected field */ T around(): autoInjectField() { return getInjectedValue(thisJoinPoint); } }

      然后做了一个具体的方面:

      public aspect SLF4jLoggerInjector extends AutoInjectAspect<Logger> { @Override public Logger getInjectedValue(JoinPoint joinPoint) { Class classInjected = joinPoint.getStaticPart() .getSignature().getDeclaringType(); Logger logger = LoggerFactory.getLogger(classInjected); return logger; } }

      然后做一个注解“@AutoInject”并把它放在你想注入的字段上。

      话虽如此,您也可以使用 IOC 容器路由。

      【讨论】:

        【解决方案3】:

        正如您所建议的,Aspect Oriented Programming (AOP) 的设计就是考虑到这种情况,所以我一定会检查一下。

        虽然在这种情况下构造函数注入确实很麻烦,但使用IOC container 可能有助于减轻您的痛苦。单例似乎很符合要求,但由于静态性,它们带来了可测试性问题 - 但您可以通过将其分成两部分来解决这个问题。

        【讨论】:

        • 把它分成2个怎么样?
        • @devoured - 这是我在 Roy Osherove 的“单元测试艺术”中读到的,关于将单例的工厂部分与静态部分分开,以便您可以在测试中替换它以返回您喜欢的内容.这有一点:weblogs.asp.net/rosherove/archive/2007/06/10/…
        【解决方案4】:

        静态单例类是我看到的大部分时间处理这个问题的方式。使用日志作为更常见的横切关注点,我总是看到我的日志调用看起来像这样:

        public class SomeClassThatDoesStuff
        {
            private static readonly ILog log = 
                  LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        
            public void DoStuff()
            {
                 log.Info("Doing something");
            }
        }
        

        你的全局时间类看起来像是一种类似的横切关注点,我会以同样的方式处理它

        我同意用 DI 做这件事会很乏味,而且不是很实用。

        【讨论】:

          【解决方案5】:

          我认为你是对的,有些类型不适合构造函数注入;有时您需要以比在一组对象中更广泛的方式跟踪某种上下文。

          当我遇到这个问题时,我经常会创建一个Singleton,但是让初始化逻辑调用依赖注入框架来做实际的实例化。这会导致特定静态方法的耦合(有些人会反对这一点) - 但同时我可以随时更改实现(也许对于某些类型在测试项目中有不同的实现,或者其他的东西)。而且我得到的好处是不必在所有对象构造函数之间传递一个非常常见的类型。

          当然,只有在很多地方确实需要类型时才应该这样做。否则,使用普通的构造函数注入。究竟何时“没关系”由您决定。

          【讨论】:

          • “但是让初始化逻辑调用依赖注入框架来做实际的实例化”这确实是个好主意。
          【解决方案6】:

          如果每个人都需要它并且它是独一无二的(就像你提到的日志一样),它绝对是一个单例。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-11
            • 1970-01-01
            • 1970-01-01
            • 2017-11-13
            • 2014-07-05
            相关资源
            最近更新 更多