Item 5: Prefer dependency injection to hardwiring resources(依賴注入優(yōu)于硬連接資源)

Many classes depend on one or more underlying(adj.潛在的糠爬,根本的) resources. For example, a spell checker depends on a dictionary. It is not uncommon to see such classes implemented as static utility classes (Item 4):

許多類依賴于一個(gè)或多個(gè)底層資源。例如鳞上,拼寫檢查程序依賴于字典琳水。常見做法是肆糕,將這種類實(shí)現(xiàn)為靜態(tài)實(shí)用工具類(Item-4):

// Inappropriate use of static utility - inflexible & untestable!
public class SpellChecker {
    private static final Lexicon dictionary = ...;
    private SpellChecker() {} // Noninstantiable
    public static boolean isValid(String word) { ... }
    public static List<String> suggestions(String typo) { ... }
}

Similarly, it’s not uncommon to see them implemented as singletons (Item 3):

類似地,我們也經(jīng)吃谛ⅲ看到它們的單例實(shí)現(xiàn)(Item-3):

// Inappropriate use of singleton - inflexible & untestable!
public class SpellChecker {
    private final Lexicon dictionary = ...;
    private SpellChecker(...) {}
    public static INSTANCE = new SpellChecker(...);
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

Neither of these approaches is satisfactory, because they assume that there is only one dictionary worth using. In practice, each language has its own dictionary, and special dictionaries are used for special vocabularies. Also, it may be desirable to use a special dictionary for testing. It is wishful thinking to assume that a single dictionary will suffice for all time.

這兩種方法都不令人滿意诚啃,因?yàn)樗鼈兗僭O(shè)只使用一個(gè)字典。在實(shí)際應(yīng)用中浑玛,每種語(yǔ)言都有自己的字典绍申,特殊的字典用于特殊的詞匯表。另外顾彰,最好使用一個(gè)特殊的字典進(jìn)行測(cè)試极阅。認(rèn)為一本字典就足夠了,是一廂情愿的想法涨享。

You could try to have SpellChecker support multiple dictionaries by making the dictionary field nonfinal and adding a method to change the dictionary in an existing spell checker, but this would be awkward, error-prone,and unworkable in a concurrent setting. Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.

你可以嘗試讓 SpellChecker 支持多個(gè)字典:首先取消 dictionary 字段的 final 修飾筋搏,并在現(xiàn)有的拼寫檢查器中添加更改 dictionary 的方法。但是在并發(fā)環(huán)境中這種做法是笨拙的厕隧、容易出錯(cuò)的和不可行的奔脐。靜態(tài)實(shí)用工具類和單例不適用于由底層資源參數(shù)化的類。

What is required is the ability to support multiple instances of the class (in our example, SpellChecker), each of which uses the resource desired by the client (in our example, the dictionary). A simple pattern that satisfies this requirement is to pass the resource into the constructor when creating a new instance. This is one form of dependency injection: the dictionary is a dependency of the spell checker and is injected into the spell checker when it is created.

所需要的是支持類的多個(gè)實(shí)例的能力(在我們的示例中是 SpellChecker)吁讨,每個(gè)實(shí)例都使用客戶端需要的資源(在我們的示例中是 dictionary)髓迎。滿足此要求的一個(gè)簡(jiǎn)單模式是在創(chuàng)建新實(shí)例時(shí)將資源傳遞給構(gòu)造函數(shù)。 這是依賴注入的一種形式:字典是拼寫檢查器的依賴項(xiàng)建丧,在創(chuàng)建它時(shí)被注入到拼寫檢查器中排龄。

// Dependency injection provides flexibility and testability
public class SpellChecker {
    private final Lexicon dictionary;
    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

The dependency injection pattern is so simple that many programmers use it for years without knowing it has a name. While our spell checker example had only a single resource (the dictionary), dependency injection works with an arbitrary(adj.任意的) number of resources and arbitrary dependency graphs. It preserves immutability (Item 17), so multiple clients can share dependent objects(assuming the clients desire the same underlying resources). Dependency injection is equally applicable to constructors, static factories (Item 1), and builders (Item 2).

依賴注入模式非常簡(jiǎn)單,許多程序員在不知道其名稱的情況下使用了多年翎朱。雖然拼寫檢查器示例只有一個(gè)資源(字典)橄维,但是依賴注入可以處理任意數(shù)量的資源和任意依賴路徑尺铣。它保持了不可變性(Item-17),因此多個(gè)客戶端可以共享依賴對(duì)象(假設(shè)客戶端需要相同的底層資源)争舞。依賴注入同樣適用于構(gòu)造函數(shù)凛忿、靜態(tài)工廠(Item-1)和構(gòu)建器(Item-2)。

A useful variant of the pattern is to pass a resource factory to the constructor.A factory is an object that can be called repeatedly to create instances of a type.Such factories embody the Factory Method pattern [Gamma95]. The Supplier<T> interface, introduced in Java 8, is perfect for representing factories. Methods that take a Supplier<T> on input should typically constrain the factory’s type parameter using a bounded wildcard type (Item 31) to allow the client to pass in a factory that creates any subtype of a specified type. For example, here is a method that makes a mosaic using a client-provided factory to produce each tile:

這種模式的一個(gè)有用變體是將資源工廠傳遞給構(gòu)造函數(shù)竞川。工廠是一個(gè)對(duì)象店溢,可以反復(fù)調(diào)用它來(lái)創(chuàng)建類型的實(shí)例。這樣的工廠體現(xiàn)了工廠方法模式 [Gamma95]流译。Java 8 中引入的 Supplier<T> 非常適合表示工廠逞怨。在輸入中接受 Supplier<T> 的方法通常應(yīng)該使用有界通配符類型(Item-31)來(lái)約束工廠的類型參數(shù),以允許客戶端傳入創(chuàng)建指定類型的任何子類型的工廠福澡。例如,這里有一個(gè)生產(chǎn)瓷磚方法驹马,每塊瓷磚都使用客戶提供的工廠來(lái)制作馬賽克:

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

Although dependency injection greatly improves flexibility and testability, it can clutter up(使雜亂) large projects, which typically(adv.通常) contain thousands of dependencies.This clutter can be all but eliminated by using a dependency injection framework, such as Dagger [Dagger], Guice [Guice], or Spring [Spring]. The use of these frameworks is beyond the scope of this book, but note that APIs designed for manual(n.手冊(cè)革砸;adj.手工的) dependency injection are trivially(adv.繁瑣地) adapted for(適用于) use by these frameworks.

盡管依賴注入極大地提高了靈活性和可測(cè)試性,但它可能會(huì)使大型項(xiàng)目變得混亂糯累,這些項(xiàng)目通常包含數(shù)千個(gè)依賴項(xiàng)算利。通過(guò)使用依賴注入框架(如 Dagger、Guice 或 Spring)泳姐,幾乎可以消除這種混亂效拭。這些框架的使用超出了本書的范圍,但是請(qǐng)注意胖秒,為手動(dòng)依賴注入而設(shè)計(jì)的 API 很容易被這些框架所使用缎患。

In summary, do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class, and do not have the class create these resources directly. Instead, pass the resources, or factories to create them, into the constructor (or static factory or builder). This practice, known as dependency injection, will greatly enhance the flexibility, reusability, and testability of a class.

總之,不要使用單例或靜態(tài)實(shí)用工具類來(lái)實(shí)現(xiàn)依賴于一個(gè)或多個(gè)底層資源的類阎肝,這些資源的行為會(huì)影響類的行為挤渔,也不要讓類直接創(chuàng)建這些資源。相反风题,將創(chuàng)建它們的資源或工廠傳遞給構(gòu)造函數(shù)(或靜態(tài)工廠或構(gòu)建器)判导。這種操作稱為依賴注入,它將大大增強(qiáng)類的靈活性沛硅、可重用性和可測(cè)試性眼刃。


Back to contents of the chapter(返回章節(jié)目錄)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市摇肌,隨后出現(xiàn)的幾起案子擂红,更是在濱河造成了極大的恐慌,老刑警劉巖朦蕴,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篮条,死亡現(xiàn)場(chǎng)離奇詭異弟头,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)涉茧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門赴恨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伴栓,你說(shuō)我怎么就攤上這事伦连。” “怎么了钳垮?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵惑淳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饺窿,道長(zhǎng)歧焦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任肚医,我火速辦了婚禮绢馍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肠套。我一直安慰自己舰涌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布你稚。 她就那樣靜靜地躺著瓷耙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刁赖。 梳的紋絲不亂的頭發(fā)上搁痛,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音乾闰,去河邊找鬼落追。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涯肩,可吹牛的內(nèi)容都是我干的轿钠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼病苗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疗垛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起硫朦,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贷腕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泽裳,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞒斩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涮总。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胸囱。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瀑梗,靈堂內(nèi)的尸體忽然破棺而出烹笔,到底是詐尸還是另有隱情,我是刑警寧澤抛丽,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布谤职,位于F島的核電站,受9級(jí)特大地震影響亿鲜,放射性物質(zhì)發(fā)生泄漏允蜈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一狡门、第九天 我趴在偏房一處隱蔽的房頂上張望陷寝。 院中可真熱鬧,春花似錦其馏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至扔仓,卻和暖如春褐奥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翘簇。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工撬码, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人版保。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓呜笑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親彻犁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叫胁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,334評(píng)論 0 10
  • 現(xiàn)在是凌晨?jī)牲c(diǎn)半左右,好久沒(méi)有熬到這么晚汞幢。無(wú)數(shù)的瑣事涌上頭來(lái)驼鹅,凌亂無(wú)序卻又那么清晰的警示著我還剩下不多的時(shí)...
    _晨曦_閱讀 209評(píng)論 0 1
  • 在風(fēng)和日麗的冬天,太陽(yáng)暖暖升起的早晨,參加一場(chǎng)特殊的婚禮输钩,放空的時(shí)間豺型,是錯(cuò)過(guò)的節(jié)點(diǎn),有遺憾买乃,卻并沒(méi)有悔恨姻氨,只是生...
    夜里飛行的貓閱讀 338評(píng)論 1 0
  • 春水初生 春林初盛 春風(fēng)十里 不如你。
    吳柯南閱讀 238評(píng)論 0 0