代码之家  ›  专栏  ›  技术社区  ›  SysAdmin

依赖注入与单例设计模式

  •  69
  • SysAdmin  · 技术社区  · 16 年前

    我们如何确定何时使用依赖注入或单例模式。 我在很多网站上读到过,他们说“在单例模式上使用依赖注入”。但我不确定我是否完全同意他们的观点。对于我的中小型项目,我肯定会直接看到单例模式的使用。

    例如记录器。我可以用 Logger.GetInstance().Log(...) 但是,与此相反,为什么我需要用记录器实例注入我创建的每个类?.

    7 回复  |  直到 11 年前
        1
  •  53
  •   Bozho    16 年前

    如果要验证测试中记录的内容,则需要进行依赖项注入。此外,记录器很少是单一的—通常每个类都有一个记录器。

    Watch this presentation 关于面向对象的可测试性设计,您将看到为什么单例是不好的。

    单重态的问题是它们代表了一个很难预测的全局状态,特别是在测试中。

    记住,一个对象可以是事实上的单例对象,但仍然可以通过依赖注入而不是通过 Singleton.getInstance() .

    我只是列出了他在演讲中的一些重要观点。在观看之后,您将获得一个完整的视角,了解为什么最好定义一个对象 什么 它的依赖关系是,但不是定义一种方式 如何创建 他们。

        2
  •  82
  •   Bryan Watts    16 年前

    单身汉就像共产主义:他们在纸面上听起来都很棒,但在实践中却伴随着问题的爆发。

    singleton模式过分强调访问对象的方便性。它完全避开了上下文,要求每个使用者使用一个appdomain范围的对象,不为不同的实现留下任何选项。它将基础设施知识嵌入到类中(调用 GetInstance() )同时准确地添加 零 表现力。它实际上降低了您的表达能力,因为如果不更改一个类使用的实现 全部的 他们当中。你不能添加一次性的功能。

    还有,上课的时候 Foo 取决于 Logger.GetInstance() , 富 有效地向消费者隐藏其依赖关系。这意味着你不能完全理解 富 或者自信地使用它,除非你阅读了它的来源并发现它依赖于 Logger . 如果你没有源代码,那就限制了你对所依赖的代码的理解和有效使用。

    使用静态属性/方法实现的singleton模式只不过是实现基础设施的一个小技巧。它在很多方面限制了你,但却没有提供比其他选择更明显的好处。你可以随心所欲地使用它,但是由于有一些可行的替代方案可以促进更好的设计,所以它不应该是推荐的实践。

        3
  •  17
  •   Péter Török    16 年前

    其他人已经很好地解释了单重态的问题。我只想添加一个关于记录器的具体案例的注释。我同意您的看法,通常通过静态访问一个日志(或者更准确地说,根日志)作为一个单例是没有问题的。 getInstance() 或 getRootLogger() 方法。(除非你想看看你正在测试的类记录了什么——但根据我的经验,我几乎记不起这种情况。对其他人来说,这可能是一个更紧迫的问题)。

    IMO通常不会担心单例日志,因为它不包含任何与您正在测试的类相关的状态。也就是说,记录器的状态(及其可能的更改)对测试类的状态没有任何影响。所以它不会让你的单元测试变得更加困难。

    另一种方法是通过构造函数注入记录器,以(几乎)应用程序中的每个类。为了保持接口的一致性,即使所讨论的类目前没有任何日志记录,也应该注入它——另一种选择是,当您在某个时候发现 现在 您需要从这个类中记录一些东西,您需要一个记录器,因此您需要为di添加一个构造函数参数,从而破坏所有客户端代码。我不喜欢这两种选择,我觉得使用di进行日志记录只会使我的生活复杂化,以遵守理论规则,而没有任何具体的好处。

    所以我的底线是: 一个类(几乎)普遍使用,但不包含与应用程序相关的状态,可以安全地实现为singleton .

        4
  •  9
  •   Scott Weinstein    16 年前

    主要是测试,但不是全部。singleton很受欢迎,因为它很容易消费,但是singleton也有很多缺点。

    • 很难测试。意思是我如何确保记录器做正确的事情。
    • 很难测试。也就是说,如果我正在测试使用记录器的代码,但它不是我测试的重点,我仍然需要确保我的测试环境支持记录器
    • 有时候你不想唱歌,但更灵活

    di使您可以轻松地使用依赖类(只需将其放入构造函数args中,系统就会为您提供它),同时还为您提供了测试和构造灵活性。

        5
  •  1
  •   kyoryu    16 年前

    如果singleton表示一个不可变的值,比如list.empty或类似的值(假设是不可变的列表),那么您应该使用singleton而不是依赖注入。

    单例的直觉检查应该是“如果这是一个全局变量而不是单例变量,我可以吗?”如果不是,则使用singleton模式覆盖全局变量,并应考虑使用不同的方法。

        6
  •  1
  •   sunwukung    16 年前

    刚刚查看了monostate文章-它是singleton的一个不错的替代品,但是它有一些wierd属性:

    class Mono{
        public static $db;
        public function setDb($db){
           self::$db = $db;
        }
    
    }
    
    class Mapper extends Mono{
        //mapping procedure
        return $Entity;
    
        public function save($Entity);//requires database connection to be set
    }
    
    class Entity{
    public function save(){
        $Mapper = new Mapper();
        $Mapper->save($this);//has same static reference to database class     
    }
    
    $Mapper = new Mapper();
    $Mapper->setDb($db);
    
    $User = $Mapper->find(1);
    $User->save();
    

    这不是很可怕吗-因为映射程序实际上依赖于数据库连接来执行save()-但是如果之前已经创建了另一个映射程序-它可以 在获取它的依赖项时跳过这一步。虽然整洁,但也有点凌乱,不是吗?