dependency injection

非常不错:http://www.martinfowler.com/

Chances are, at some point in your learning, you‘ve come across the term, "dependency injection." If you‘re still relatively early in your learning, you likely formed a confused expression and skipped over that part. Still, this is an important aspect of writing maintainable (and testable) code. In this article, I‘ll explain it in as simple a way as I‘m capable of.

An Example

Let‘s jump into a fairly generic piece of code, and discuss its short-comings.


01

02

03

04

05

06

07

08

09

10

11

12

13

14

class Photo {

    /**

     * @var PDO The connection to the database

     */

    protected $db;

    /**

     * Construct.

     */

    public function __construct()

    {

        $this->db = DB::getInstance();

    }

}

At first glance, this code might seem fairly harmless. But consider the fact that, already, we‘ve hardcoded a dependency: the database connection. What if we want to introduce a different persistence layer? Or, think about it this way: why should the Photo object be communicating with outside sources? Doesn‘t that violate the concept of separation of concerns? It certainly does. This object shouldn‘t be concerned with anything that isn‘t directly related to a Photo.

The basic idea is that your classes should be responsible for one thing only. With that in mind, it shouldn‘t be responsible for connecting to databases and other things of that nature.

Think of objects in the same way that you think of your pet. The dog doesn‘t decide when he goes outside for a walk or to play at the park. You do! It‘s not his place to make these decisions.

Let‘s regain control of the class, and, instead, pass in the database connection. There are two ways to accomplish this: constructor and setter injection, respectively. Here‘s examples of both:

Constructor Injection


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

class Photo {

    /**

     * @var PDO The connection to the database

     */

    protected $db;

    /**

     * Construct.

     * @param PDO $db_conn The database connection

     */

    public function __construct($dbConn)

    {

        $this->db = $dbConn;

    }

}

$photo = new Photo($dbConn);

Above, we‘re injecting all required dependencies, when the class‘s constructor method runs, rather than hardcoding them directly into the class.

Setter Injection


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

class Photo {

    /**

     * @var PDO The connection to the database

     */

    protected $db;

    public function __construct() {}

    /**

     * Sets the database connection

     * @param PDO $dbConn The connection to the database.

     */

    public function setDB($dbConn)

    {

        $this->db = $dbConn;

    }

}

$photo = new Photo;

$photo->setDB($dbConn);

With this simple change, the class is no longer dependent upon any specific connection. The outside system retains complete control, as should be the case. While it may not be immediately visible, this technique also makes the class considerably easier to test, as we can now mock the database, when calling the setDB method.

Even better, if we later decide to use a different form of persistence, thanks to dependency injection, it‘s a cinch.

"Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields."

The Rub

There‘s one problem with using setter injection in this way: it makes the class considerably more difficult to work with. The user now must be fully aware of the class‘s dependencies, and must remember to set them, accordingly. Consider, down the line, when our fictional class requires a couple more dependencies. Well, following the rules of the dependency injection pattern, we‘d have to do:


1

2

3

4

$photo = new Photo;

$photo->setDB($dbConn);

$photo->setConfig($config);

$photo->setResponse($response);

Yikes; the class may be more modular, but we‘ve also piled on confusion and complexity. Before, the user could simply create a new instance of Photo, but, now, he has to remember to set all of these dependencies. What a pain!

The Solution

The solution to this dilemma is to create a container class that will handle the brunt of the work for us. If you‘ve ever come across the term, "Inversion of Control (IoC)," now you know what they‘re referring to.

DefinitionIn software engineering, Inversion of Control (IoC) is an object-oriented programming practice where the object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis.

This class will store a registry of all the dependencies for our project. Each key will have an associated lambda function that instantiates the associated class.

There are a couple ways to tackle this. We could be explicit, and store methods, such as newPhoto:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

// Also frequently called "Container"

class IoC {

    /**

     * @var PDO The connection to the database

     */

    protected $db;

    /**

     * Create a new instance of Photo and set dependencies.

     */

    public static newPhoto()

    {

        $photo = new Photo;

        $photo->setDB(static::$db);

        // $photo->setConfig();

        // $photo->setResponse();

        return $photo;

    }

}

$photo = IoC::newPhoto();

Now, $photo will be equal to a new instance of the Photo class, with all of the required dependencies set. This way, the user doesn‘t have to remember to set these dependencies manually; he simply calls thenewPhoto method.

The second option, rather than creating a new method for each class instantiation, is to write a generic registry container, like so:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

class IoC {

    /**

     * @var PDO The connection to the database

     */

    protected static $registry = array();

    /**

     * Add a new resolver to the registry array.

     * @param  string $name The id

     * @param  object $resolve Closure that creates instance

     * @return void

     */

    public static function register($name, Closure $resolve)

    {

        static::$registry[$name] = $resolve;

    }

    /**

     * Create the instance

     * @param  string $name The id

     * @return mixed

     */

    public static function resolve($name)

    {

        if ( static::registered($name) )

        {

            $name = static::$registry[$name];

            return $name();

        }

        throw new Exception(‘Nothing registered with that name, fool.‘);

    }

    /**

     * Determine whether the id is registered

     * @param  string $name The id

     * @return bool Whether to id exists or not

     */

    public static function registered($name)

    {

        return array_key_exists($name, static::$registry);

    }

}

Don‘t let this code scare you; it‘s really very simple. When the user calls the IoC::register method, they‘re adding an id, such as photo, and its associated resolver, which is just a lambda that creates the instance and sets any necessary dependencies on the class.

Now, we can register and resolve dependencies through the IoC class, like this:


01

02

03

04

05

06

07

08

09

10

11

// Add `photo` to the registry array, along with a resolver

IoC::register(‘photo‘, function() {

    $photo = new Photo;

    $photo->setDB(‘...‘);

    $photo->setConfig(‘...‘);

    return $photo;

});

// Fetch new photo instance with dependencies set

$photo = IoC::resolve(‘photo‘);

So, we can observe that, with this pattern, we‘re not manually instantiating the class. Instead, we register it with the IoC container, and then request a specific instance. This reduces the complexity that we introduced, when we stripped the hardcoded dependencies out of the class.


1

2

3

4

5

// Before

$photo = new Photo;

// After

$photo = IoC::resolve(‘photo‘);

Virtually the same number of characters, but, now, the class is significantly more flexible and testable. In real-world usage, you‘d likely want to extend this class to allow for the creation of singletons as well.

Embracing Magic Methods

If we want to reduce the length of the IoC class even further, we can take advantage of magic methods - namely __set() and __get(), which will be triggered if the user calls a method that does not exist in the class.


01

02

03

04

05

06

07

08

09

10

11

12

13

class IoC {

    protected $registry = array();

    public function __set($name, $resolver)

    {

        $this->registry[$name] = $resolver;

    }

    public function __get($name)

    {

        return $this->registry[$name]();

    }

}

Popularized by Fabien Potencier, this is a super-minimal implementation - but it‘ll work. Whether or not__get() or set() runs will be dependent upon whether the user is setting a value or not.

Basic usage would be:


01

02

03

04

05

06

07

08

09

10

11

$c = new IoC;

$c->mailer = function() {

  $m = new Mailer;

  // create new instance of mailer

  // set creds, etc.

  

  return $m;

};

// Fetch, boy

$mailer = $c->mailer; // mailer instance

Thanks for reading!

时间: 2024-10-11 07:57:23

dependency injection的相关文章

[LINK]List of .NET Dependency Injection Containers (IOC)

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx I'm trying to expand my mind around dependency injection in .NET (beyond the two frameworks I've personally used) and an starting to put together a list of .NET Dependency I

IoC(Inversion of Control)控制反转和 DI(Dependency Injection)依赖注入

首先想说说IoC(Inversion of Control,控制倒转).这是spring的核心,贯穿始终.所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系.这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好.qq号.电话号.ip号.iq号---,想办法认识她们,投其所好送其所要,然后嘿嘿--这个过程是复杂深奥的,我们必须自己设计和面对每个环节.传统的程序开发也是如此,在

Dependency Injection 筆記 (4)

續上集未完的相關設計模式... Composite 模式延續先前的電器比喻.現在,如果希望 UPS 不只接電腦,還要接電風扇.除濕機,可是 UPS 卻只有兩個電源輸出孔,怎麼辦? 我們可以買一條電源延長線,接在 UPS 上面.如此一來,電風扇.除濕機.和電腦便都可以同時插上延長線的插座了.這裡的電源延長線,即類似Composite Pattern(組合模式),因為電源延長線本身又可以再連接其他不同廠牌的延長線(這又是因為插座皆採用相同介面),如此不斷連接下去. 呃….延長線的比喻有個小問題:它在

Dependency Injection 筆記 (1)

<.NET 相依性注入>連載 (1) 本文從一個基本的問題開始,點出軟體需求變動的常態,以說明為什麼我們需要學習「相依性注入」(dependency injection:簡稱 DI)來改善設計的品質.接著以一個簡單的入門範例來比較沒有使用 DI 和改寫成 DI 版本之後的差異,並討論使用 DI 的時機.目的是讓讀者先對相關的基礎概念有個概括的理解,包括可維護性(maintainability).寬鬆耦合(loose coupling).控制反轉(inversion of control).動態

(You Can Hack It, Architecture and Design) =&gt; { Dependency Injection; }

(You Can Hack It, Architecture and Design) => { Dependency Injection; }

Scala 深入浅出实战经典 第57讲:Scala中Dependency Injection实战详解

王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 腾讯微云:http://url.cn/TnGbdC 360云盘:http://yunpan.cn/cQ4c2UALDjSKy 访问密码 45e2土豆:http://www.tudou.com/programs/view/5LnLNDBKvi8/优酷:http://v.youku.com/v_show/id_

Introduction to dependency injection with unityContainer

/* By Dylan SUN */ This article is just a simple introduction to unityContainer. If you have already used unityContainer as a dependency injection or IoC (Inversion of control) library, this article is not for you. As you know, dependency injection i

Json.NET Updates: Merge, Dependency Injection, F# and JSONPath Support

Json.NET 6.0 received 4 releases this year, the latest last week. Over these releases, several new features have been added, including several F# specific features, support for JSONPath querying, ability to integrate with Dependency Injection framewo

Dependency Injection 筆記 (2)

續上集,接著要說明如何運用 DI 來讓剛才的範例程式具備執行時期切換實作類別的能力. 入門範例—DI 版本 為了讓 AuthenticationService 類別能夠在執行時期才決定要使用 EmailService 還是 ShortMessageService 來發送驗證碼,我們必須對這些類別動點小手術,把它們之間原本緊密耦合的關係鬆開——或者說「解耦合」.有一個很有效的工具可以用來解耦合:介面(interface). 說得更明白些,原本 AuthenticationService 是相依於特

[Vue + TS] Use Dependency Injection in Vue Using @Inject and @Provide Decorators with TypeScript

Vue 2.2 introduced a simple dependency injection system, allowing you to use provide and inject in your component options. This lesson shows you how to use them using the @Inject and @Provide decorators in tandem! When you want to provide some servic