许多类依赖一个或多个底层资源。打个比方,拼写检查功能依赖于字典。将这些类实现为静态工具类并不罕见。(item4)

相似的,将这些类实现为单例也并不罕见(item3) :

这些方法都不够好,因为它们声明了只有一个字典值得使用。实际上,每个语言都有它自己的字典,特别的字典有着特别的词汇。同时,可能需要一个特别的字典用来测试。声明一个字典在任何时候都能满足需求,这真是痴心妄想!
你可以尝试通过使字典属性不为final来使SpellChecker支持多个字典,同时添加一个为不同的Spell Checker使用不同字典的方法,但是这样的做法是笨拙的,是易错的,在并发的情况下是不能正常工作的。静态工具类和单例不适合其行为由底层参数化的类。
我们需要支持该类的多个实例的能力(例子中指的是SpellChecker),每个实例都使用客户端所需的资源(例子指的是字典)。满足这个需求的一个简单模式是当创建一个新实例的时候,将资源传入构造方法。这是依赖注入的一种形式:字典是spell checker的一个依赖,当spell checker创建的时候将字典注入。

依赖注入模式如此简单以至于许多程序员使用它好几年了但是不知道它的名字。虽然我们的spell checker例子只有一个单一的资源(字典)时,但依赖注入可以使用任意数量的资源和任意的依赖关系图。它保留了不可变性(item17),所以多个客户点可以分享依赖对象(声明客户端需要相同地层资源)。依赖注入适用于构造方法,静态工厂方法(item1) ,和建造者(item2)。
该模式的一个有用的变种是将资源工厂引入构造方法。一个工厂是一个可以被重复调用来创建实例的一种对象类型。这样的工厂体现了工厂方法模式。在Java8推出的接口Supplier<T>,非常好地代表了工厂。在输入上采用Supplier<T>的方法通常应该使用有界通配符(item31)来约束工厂的类型参数 ,以允许客户端传入该工厂, 如果指定类型则创建任意子类型。举个例子,这是一个使用客户端提供的工厂生成马赛克来生成每个图块的方法:

虽然依赖注入极大的提高了灵活性和可测试性,但是往往一个大项目包含了成千上万的依赖,它会堆满。这样的干扰通过一个依赖注入的框架就能消除,比如Dagger,Guice或Spring。这些框架的使用范围超出了这本书的范围,但是注意到,为了手动依赖注入而设计的API显而易见地适合这些框架。
总之,不要使用单例或静态工具类来实现依赖其行为影响类的一个或多个底层资源的类,不要让这些类直接创建这些资源。相反,要传入资源或工厂到构造方法(或静态工厂或建造者)来创建他们。实际上,依赖注入将极大地提高一个类地灵活性,可复用性和可测试性
本文写于2018.11.23,历时1天
网友评论