Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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
Design patterns 依赖注入&;单件设计模式_Design Patterns_Oop_Dependency Injection - Fatal编程技术网

Design patterns 依赖注入&;单件设计模式

Design patterns 依赖注入&;单件设计模式,design-patterns,oop,dependency-injection,Design Patterns,Oop,Dependency Injection,我们如何确定何时使用依赖注入或单例模式。 我在很多网站上读到过,他们说“在单例模式上使用依赖注入”。但是我不确定我是否完全同意他们。对于我的中小型项目,我肯定可以直接使用singleton模式 例如,记录器。我可以使用Logger.GetInstance().Log(…) 但是,与此相反,为什么我需要使用记录器的实例注入我创建的每个类?如果您想验证测试中记录的内容,则需要依赖项注入。此外,记录器很少是单独的-通常每个类都有一个记录器 关于面向对象的可测试性设计,您将看到为什么单例是不好的 单例的

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

例如,记录器。我可以使用
Logger.GetInstance().Log(…)

但是,与此相反,为什么我需要使用记录器的实例注入我创建的每个类?

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

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

单例的问题是,它们代表了一种难以预测的全局状态,特别是在测试中

请记住,对象可以是事实上的单例对象,但仍然可以通过依赖项注入而不是通过
singleton.getInstance()
获得


我只是列出了Misko Hevery在演讲中提出的一些重要观点。观看之后,您将全面了解为什么让一个对象定义其依赖项是什么更好,而不是定义如何创建它们。

主要是关于测试,但不完全是关于测试。辛格尔顿夫妇之所以受欢迎,是因为他们很容易消费,但他们也有很多缺点

  • 很难测试。意思是如何确保记录器做正确的事情
  • 很难测试。这意味着,如果我正在测试使用记录器的代码,但它不是我测试的重点,那么我仍然需要确保我的测试环境支持记录器
  • 有时你不想要一个新的,但更灵活

DI让您可以轻松地使用依赖类—只需将其放在构造函数参数中,系统就会为您提供—同时为您提供测试和构造灵活性。

单例类似于共产主义:它们在纸面上听起来都很棒,但在实践中却会出现问题

单例模式过分强调访问对象的方便性。它完全避开了上下文,要求每个使用者使用AppDomain范围的对象,不给不同的实现留下任何选项。它将基础结构知识嵌入到类中(调用
GetInstance()
),同时完全不增加表达能力。它实际上降低了您的表达能力,因为您无法更改一个类使用的实现,而不更改所有类的实现。您不能简单地添加一次性功能

此外,当类
Foo
依赖于
Logger.GetInstance()
时,
Foo
有效地向使用者隐藏其依赖关系。这意味着您无法完全理解
Foo
或自信地使用它,除非您阅读它的源代码并发现它依赖于
Logger
。如果您没有源代码,这将限制您对所依赖的代码的理解和有效使用


使用静态属性/方法实现的单例模式只不过是对实现基础设施的一种攻击。它在很多方面限制了你,但对其他选择没有明显的好处。您可以随心所欲地使用它,但由于有一些可行的替代方案可以促进更好的设计,因此不应将其作为推荐做法。

其他人已经很好地解释了单例的一般问题。我想补充一点关于Logger的具体案例。我同意您的看法,通过静态
getInstance()
getRootLogger()
方法,以单例方式访问记录器(确切地说是根记录器)通常不是问题。(除非你想看看你正在测试的类记录了什么——但根据我的经验,我几乎记不起有必要这样做的情况。另外,对于其他人来说,这可能是一个更紧迫的问题)

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

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


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

关于使用单例而不是依赖项注入的唯一时间,是单例表示不可变的值,例如List.Empty或类似值(假设不可变列表)


一个单体的肠道检查应该是“如果这是一个全局变量而不是一个单独的变量,我会不会没事?”如果不是,你使用单模式来对一个全局变量进行写,并且应该考虑一个不同的方法。

< P> Singleton还有其他的替代方案:代理和单态模式。


刚刚查看了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();
这不是很吓人吗?因为映射程序实际上依赖于数据库