一. 什么是依賴注入桌粉?

此文是本人翻譯的來自國(guó)外某網(wǎng)站一篇文章 What is Dependency Injection?,第一次翻譯蒸绩,各位見諒

這篇文章是一系列關(guān)于依賴注入和PHP輕量級(jí)容器實(shí)現(xiàn)文章中的一部分:
Part 1: What is Dependency Injection?
Part 2: Do you need a Dependency Injection Container?
Part 3: Introduction to the Symfony Service Container
Part 4: Symfony Service Container: Using a Builder to create Services
Part 5: Symfony Service Container: Using XML or YAML to describe Services
Part 6: The Need for Speed

今天,我一開始不會(huì)講容器铃肯,我希望先通過一些具體的實(shí)例來介紹一下依賴注入的理念以及其所嘗試解決的問題和它能給開發(fā)者帶來的好處患亿。如果你已經(jīng)了解依賴注入,你可以跳過這篇文章去看下一篇。
依賴注入可能是我知道的最簡(jiǎn)單的設(shè)計(jì)模式之一步藕,很可能你已經(jīng)使用過惦界,但是同時(shí)也是最難解釋的,原因可能是大多數(shù)介紹依賴注入的文章用的例子都比較無聊咙冗。我想了一個(gè)比較適合PHP領(lǐng)域的例子沾歪,因?yàn)镻HP主要用在web開發(fā),所以讓我們來看一個(gè)簡(jiǎn)單的web實(shí)例雾消。
為了解決http協(xié)議無狀態(tài)的問題,web應(yīng)用需要一種在web請(qǐng)求之間記錄用戶信息的方法狂窑,簡(jiǎn)單的通過cookie或者session都能解決:

$_SESSION['language'] = 'fr';

上面的代碼把用戶的語言存在了session變量里面桑腮。這樣,對(duì)于同一個(gè)用戶的請(qǐng)求旨巷,其所使用的語言就會(huì)被存儲(chǔ)在$_SESSION數(shù)組里面采呐,我們可以這樣獲雀槠铩:

$user_language = $_SESSION['language'];

由于依賴注入只在面向?qū)ο蟮氖澜缋镉幸饬x,我們假裝我們有一個(gè)叫SessionStorage的類封裝了處理session的方法:

class SessionStorage
{
  function __construct($cookieName = 'PHP_SESS_ID')
  {
    session_name($cookieName);
    session_start();
  }
  function set($key, $value)
  {
    $_SESSION[$key] = $value;
  }
  function get($key)
  {
    return $_SESSION[$key];
  }
  // ...
}

...和一個(gè)提供高級(jí)接口的易用的User類

class User
{
  protected $storage;

  function __construct()
  {
    $this->storage = new SessionStorage();
  }

  function setLanguage($language)
  {
    $this->storage->set('language', $language);
  }

  function getLanguage()
  {
    return $this->storage->get('language');
  }

  // ...
}

這些類足夠簡(jiǎn)單煤率,使用User類也非常容易:

$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();

到目前為止蝶糯,一切都很好...除非你想要更多的靈活性昼捍。萬一你想要改變session里面的cookie名字呢肢扯?你可能會(huì)使用下面這些方法:

  1. 在SessionStorage構(gòu)造器里面硬編碼名字
  class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage('SESSION_ID');
      }

      // ...
    }
  1. 在User類外面定義一個(gè)常量
 define('STORAGE_SESSION_NAME', 'SESSION_ID');
 class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage(STORAGE_SESSION_NAME);
      }

      // ...
    }
  1. 在User類構(gòu)造器里面?zhèn)鬟f一個(gè)名字作為參數(shù)
 class User
    {
      function __construct($sessionName)
      {
        $this->storage = new SessionStorage($sessionName);
      }

      // ...
    }

    $user = new User('SESSION_ID');
  1. 在User類構(gòu)造器里面?zhèn)鬟f一個(gè)數(shù)組選項(xiàng)
    class User
    {
      function __construct($storageOptions)
      {
        $this->storage = new SessionStorage($storageOptions['session_name']);
      }

      // ...
    }

    $user = new User(array('session_name' => 'SESSION_ID'));

以上的所有選擇都很爛乍钻,硬編碼名字沒有真正解決問題因?yàn)槟阋院罂赡茈S時(shí)會(huì)改變注意,你還得更改User類多糠。使用常量也是一個(gè)壞注意欢摄,因?yàn)槟阌忠蕾嚵艘粋€(gè)常量。通過傳遞一個(gè)數(shù)組參數(shù)可能是一個(gè)好的解決方案析蝴,但是依然不太好闷畸,它把User構(gòu)造器和一個(gè)和它本身不相關(guān)的東西耦合了吞滞。
而且還有一個(gè)問題沒法容易搞定:我怎么換掉SessionStorage類?比方說殿漠,用一個(gè)mock對(duì)象去測(cè)試佩捞,或者你想把session保存在數(shù)據(jù)庫(kù)或內(nèi)存里面一忱。在目前的代碼里面除非你更改User類,否則無法實(shí)現(xiàn)票渠。

依賴注入

不要在User類里面創(chuàng)建SessionStorage對(duì)象芬迄,我們?cè)陬愅饷鎰?chuàng)建SessionStorage對(duì)象,然后通過構(gòu)造函數(shù)把其作為參數(shù)傳進(jìn)來:

class User
{
  function __construct($storage)
  {
    $this->storage = $storage;
  }

  // ...
}

這就是依賴注入,就是這些择诈!

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);

現(xiàn)在出皇,配置一個(gè)session存儲(chǔ)對(duì)象非常簡(jiǎn)單了郊艘,替換它也很容易,不用改變User類也可以實(shí)現(xiàn)其他功能畏浆。
Pico Container website 這樣形容依賴注入:“依賴注入就是通過構(gòu)造器刻获、方法瞎嬉、屬性獲取所需要的元素”
依賴注入不僅僅局限于此:

  • 構(gòu)造器注入:
  class User
    {
      function __construct($storage)
      {
        $this->storage = $storage;
      }

      // ...
    }
  • Setter注入:
  class User
    {
      function setSessionStorage($storage)
      {
        $this->storage = $storage;
      }

      // ...
    }
  • 屬性注入:
  class User
    {
      public $sessionStorage;
    }

    $user->sessionStorage = $storage;

一般來說氧枣,構(gòu)造器注入最適合必要依賴,就像例子里面那樣扎谎,Setter注入比較適合可選依賴烧董,比如說緩存對(duì)象。當(dāng)今预吆,很多現(xiàn)代PHP框架大量使用依賴注入提供一系列既解耦又有凝聚力的組件:

// symfony: A constructor injection example
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(array('database' => 'session', 'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage, array('default_culture' => 'en'));
// Zend Framework: A setter injection example
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
  'auth'     => 'login',
  'username' => 'foo',
  'password' => 'bar',
  'ssl'      => 'ssl',
  'port'     => 465,
));
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);

如果你想了解更多關(guān)于依賴注入的東西啡浊,我強(qiáng)烈建議你讀一讀Martin Fowler introduction 或者 Jeff More presentation胶背。你也可以看看我去年關(guān)于依賴注入的演講,這里講了更多細(xì)節(jié)

好了钳吟,就說這么多了,我希望你現(xiàn)在對(duì)依賴注入有更好的理解坝茎,本系列的下一章我會(huì)講關(guān)于依賴注入容器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嗤放,一起剝皮案震驚了整個(gè)濱河市壁酬,隨后出現(xiàn)的幾起案子恨课,更是在濱河造成了極大的恐慌剂公,老刑警劉巖吊宋,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璃搜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绿贞,警方通過查閱死者的電腦和手機(jī)橘原,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門趾断,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人增显,你說我怎么就攤上這事同云《赂梗” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵旱易,是天一觀的道長(zhǎng)阀坏。 經(jīng)常有香客問我笆檀,道長(zhǎng),這世上最難降的妖魔是什么士修? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上箫老,老公的妹妹穿的比我還像新娘。我一直安慰自己阔籽,他們只是感情好牲蜀,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布涣达。 她就那樣靜靜地躺著,像睡著了一般匆篓。 火紅的嫁衣襯著肌膚如雪寇窑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天窗市,我揣著相機(jī)與錄音咨察,去河邊找鬼缎浇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛二蓝,可吹牛的內(nèi)容都是我干的指厌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼鸥诽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了拳昌?” 一聲冷哼從身側(cè)響起钠龙,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤碴里,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后羹膳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體根竿,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蠢壹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了图贸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冕广。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撒汉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挠阁,到底是詐尸還是另有隱情溯饵,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布隘谣,位于F島的核電站啄巧,受9級(jí)特大地震影響掌栅,放射性物質(zhì)發(fā)生泄漏猾封。R本人自食惡果不足惜噪珊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一卿城、第九天 我趴在偏房一處隱蔽的房頂上張望铅搓。 院中可真熱鬧,春花似錦多望、人聲如沸氢烘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜀踏。三九已至,卻和暖如春颅痊,著一層夾襖步出監(jiān)牢的瞬間局待,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工舰罚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留重绷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓愤钾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親杂瘸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伙菊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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