dependency injection和单例devise模式
我们如何确定何时使用dependency injection或单例模式。 我读了很多网站,他们说:“使用dependency injection单身模式”。 但是我不确定我是否完全同意他们。 对于我的中小规模项目,我绝对可以看到直接使用单例模式。
例如logging器。 我可以使用Logger.GetInstance().Log(...)
但是,而不是这个,为什么我需要注入我创build的每个类,logging器的实例?
如果您想validation在testing中login的内容,则需要dependency injection。 此外,一个logging器很less是一个单身人士 – 通常你每个class级都有一个logging器。
观看这个关于面向对象devise的可测性的介绍,你会明白为什么单例是不好的。
单身人士的问题是他们代表了一个难以预测的全球状态,特别是在testing中。
请记住,一个对象可以是事实上的单例,但仍然可以通过dependency injection获得,而不是通过Singleton.getInstance()
。
我只是列举了Misko Hevery在他的演讲中提出的一些重要观点。 看完之后,你将会看到为什么最好有一个对象定义它的依赖关系,而不是定义一个如何创build它们的方法。
单身人士就像共产主义:他们在纸上听起来很好,但在实践中出现问题。
单身模式强调访问对象的难易程度。 它通过要求每个消费者使用一个AppDomain范围的对象完全避开了上下文,并没有为各种实现留下任何select。 它将基础设施知识embedded您的类中(调用GetInstance()
),同时增加零expression能力。 它实际上减弱了你的performance力,因为你不能改变一个类所使用的实现而不改变它们。 你根本不能添加一次性的function。
另外,当Foo
类依赖于Logger.GetInstance()
, Foo
有效地隐藏了消费者的依赖关系。 这意味着你不能完全理解Foo
或者放心地使用它,除非你阅读它的源代码并且发现它依赖于Logger
。 如果你没有源代码,那就限制了你理解和有效使用你所依赖的代码的能力。
用静态属性/方法实现的单例模式,只不过是实施基础架构而已。 它以各种各样的方式限制你,而不是提供可select的好处。 你可以使用它,不过你可以使用它,但是因为有可行的替代方法来促进更好的devise,所以它不应该是一个推荐的做法。
其他人也很好地解释了一般单身人士的问题。 我只想添加关于Logger的具体情况的说明。 我同意你通过一个静态的getInstance()
或者getRootLogger()
方法来访问一个Logger(或者根logging器),通常不是一个单身问题。 (除非你想看看你正在testing的课程logging什么 – 但根据我的经验,我很难回忆起这种情况,在这种情况下,对于其他人来说,这可能是一个更紧迫的问题)。
国际海事组织通常单身logging器不是一个担心,因为它不包含任何有关你正在testing的类。 也就是说,logging器的状态(及其可能的变化)对被testing类的状态没有任何影响。 所以这不会让你的unit testing更加困难。
另一种方法是通过构造函数将logging器注入(几乎)应用程序中的每个类。 为了保持接口的一致性,即使有问题的类目前没有logging任何东西,也应该注入 – 另一种方法是,当你发现某个时刻你需要从这个类中logging一些东西时,你需要一个logging器。你需要为DI添加一个构造函数参数,打破所有的客户端代码。 我不喜欢这两种select,而且我觉得使用DI进行logging会使我的生活复杂化,以便遵从理论规则,而没有任何具体的好处。
所以我的底线是: 一个(几乎)普遍使用的类,但不包含与您的应用程序相关的状态,可以安全地实现为Singleton 。
这主要是,但不是testing的entirly。 Singltons很受欢迎,因为消费很容易,但单身人士有很多缺点。
- 很难testing。 这意味着如何确保logging仪做对了事情。
- 很难testing。 这意味着如果我testing使用logging器的代码,但这不是我testing的重点,我仍然需要确保我的testing环境支持logging器
- 有时你不想要一个单身汉,但更灵活
DI为您提供了简单的依赖类的消耗 – 只需将其放入构造函数参数中,系统为您提供帮助 – 同时为您提供testing和构build的灵活性。
如果Singleton表示一个不可变的值,例如List.Empty或类似的(假设不可变列表),那么关于唯一的一次应该使用Singleton而不是dependency injection。
一个单身人士的肠道检查应该是“如果这是一个全局variables而不是单身人士,我会好吗? 如果不是,你使用Singleton模式来遍历一个全局variables,应该考虑一个不同的方法。
刚刚签出了Monostate文章 – 这是一个很好的替代Singleton,但它有一些奇怪的属性:
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();
不是这种可怕的 – 因为Mapper真的依赖于数据库连接来执行save() – 但是如果另外一个映射器已经被创build了,它可以跳过这个步骤来获取它的依赖关系。 虽然整洁,这也是一种凌乱是不是?
Singleton还有其他的select:Proxy和MonoState模式。
resources/articles/SingletonAndMonostate.html
代理模式如何被用来取代单身人士?