php 依赖注入 数据库切换_PHP 依赖注入

关于Dependency

Injection注入,可以参考下面的链接:

http://fabien.potencier.org/article/11/what-is-dependency-injection

http://www.devshed.com/c/a/PHP/The-Dependency-Injection-Design-Pattern-in-PHP-5/

PHP Dependency

Injection

PHP 依赖注入

by Ryan on January 8, 2009

Dependency injection is the answer to more

maintainable, testable, modular code.

依赖注入是对于要求更易维护,更易测试,更加模块化的代码的答案。

Every project has dependencies and the

more complex the project is the more dependencies it will most

likely have. The most common dependency in today’s web

application is the database and chances are if it goes down the app

will all together stop working. That is because the code is

dependent on the database server… and that is perfectly fine. Not using a database server because it could one day crash is a bit

ridiculous. Even though the dependency has its flaws, it

still makes life for the code, and thus the developer, a lot

easier.

每个项目都有依赖(外界提供的输入), 项目越复杂,越需要更多的依赖。在现今的网络应用程序中,最常见的依赖是数据库,其风险在于,一旦数据库暂停运行,那么整个程序也因此而停止运行。这是因为代码依赖数据库服务器。。。这已足够。因为数据库服务器有时会崩溃而弃用它是荒谬的。尽管依赖有其自身的瑕疵,它仍然使代码,因而也使程序开发人员的生活更容易些。

The problem with most dependencies its the

way that code handles and interacts with them, meaning, the problem

is the code and not the dependency. If you are not using dependency

injection, chances are your code looks something like

this:

对于大多依赖而言,问题在于代码如何处理它们及与它们交往。也就是说,问题是代码本身而非依赖。如果你没有使用依赖注入,有机会遇到下列代码:

class Book {

publicfunction

__construct(){

$registry =

RegistrySingleton::getInstance();

$this->_databaseConnection=$registry->databaseConnection;

//

or

global$databaseConnection;

$this->_databaseConnection=$databaseConnection;

}

}

The book object now is given full access

to the database once it is constructed. That is good, the

book needs to be able to talk to the database and pull data. The problem lies in the way the book gained its access. In

order for the book to be able to talk to the database the code must

have an outside variable named $databaseConnection, or worse, it

must have a singleton pattern class (registry) object containing a

record for a databaseConnection. If these don’t exist the

book fails, making this code is far from modular.

现在对象book一旦建立,就已经完全连到数据库。这没什么不好,book需要跟数据库交谈并取得数据。问题在于book取得其接入的方法。要使book能够与数据库交谈,代码必须有一个外来的变量$databaseConnect, 更糟糕的是,它必须有个singleton类型类(registry)的对象,其包含一个databaseConnection的记录。如果这些不存在的话,book会无法运行,这就使得这些代码远远不够模块化。

This raises the question, how exactly does

the book get access to the database? This is where inversion

of control comes in.

这就提出一个问题,到底book如何接入数据库?这就是IoC出现的原因了。

In Hollywood a struggling actor does not

call up Martin Scorsese and ask for a role in his next film. No, the opposite happens. Martin Scorsese calls up the broke

actor and asks him to play the main character in his next

movie. Objects are struggling actors, they do not get to pick

the roles they play, the director needs to tell them what to

do. Objects do not get to pick the outside systems they

interact with, instead, the outside systems are given to the

objects. Remember this as Inversion of

Control: The Hollywood Way.

在好莱坞,一个濒临绝境的演员不会打电话给Martin Scoresese要求在他的下个影片中扮演一个角色。绝不会这样,实际正相反。Martin Scorese 会打电话给这个困顿的演员并邀请他在下一部影片中扮演重要的角色。对象是挣扎中的演员,他们不会去捡自己所扮演的角色,导演需要告诉他们去做什么。对象不会到它所接触的外界挑选系统,相反,外界系统会被赋予对象。记住这就是IoC( Inversion of

Control):好莱坞的做法。

This is how a developer tells his objects

how to interact with outside

dependencies:

如下是开发人员告诉他的对象如何与外界的依赖打交道:

class Book {

publicfunction

__construct(){}

publicfunction

setDatabaseConnection($databaseConnection){

$this->_databaseConnection=$databaseConnection;

}

}

$book=new Book();

$book->setDatabase($databaseConnection);

This code allows for the book class to be

used in any web app. The Book is no longer dependent on

anything other than the developer supplying a database shortly

after object creation.这代码允许这个book类能在任何的网页程序中使用。此book不再依赖任何事项,除了程序开发者要在对象创建后立即要提供一个数据库。

This is, at its finest, dependency

injection. There are two common practices of injecting

dependencies. The first being constructor injection and the

second being setter injection. Constructor injection involves

passing all of the dependencies as arguments when creating a new

object. The code would look something like

this:

这就是最佳方法—依赖注入。有两种常用的依赖注入的方式。一种是

constructor (注:构造函数。这种译法似乎并不恰当,类中此方法更多是用来对某些属性进行初始化)注入,一种是setter 注入。Constructor注入涉及到将所有依赖作为参数,传递给新创建的对象。看起来像这样:

$book=new Book($databaseConnection,$configFile);

The more dependencies an object has, the

messier this construction becomes. There are other reasons

why this is a bad approach, involving ideas around code reusability

and constructors doing work.

对象的依赖越多,construction就会变得越杂乱。这里还有其他的原因为什么这是一个坏方法,其涉及到包括代码重用和constructors要做的工作。

This leaves us with other method of

dependency injection, called setting injection, which involves

creating a public method inside the object for the dependencies

that need injection.

这就给我们留下其他的方法进行依赖注入,称为设置注入,包含在需要注入依赖的对象里创建一个公共方法:

$book=new Book();

$book->setDatabase($databaseConnection);

$book->setConfigFile($configFile);

This is easy to follow, but it leads

writing more and more code for your application. When a book

object is created three lines of code are required. If

we have to inject another dependency, a 4th line of code is now

needed. This gets messy quickly.

这很容易实现,但是它会导致为您的程序写大量代码。当创建一个book对象时,需要三行代码。如果我们必须注入另外的依赖时,必须增加第四行代码。这很快变得一团糟。

The answer to this problem is a factory,

which is class that is designed to create and then inject all the

dependencies needed for an object. Here is an

example:

此问题的对策是一个工厂factory,它是一个类,用来创建然后注入一个对象的所有依赖。举例如下:

class Factory {

public

static $_database;

public

static function

makeBook(){

$book=new Book();

$book->setDatabase(self::$_database);

// more

injection...(更多注入)

return$book;

}

}

And then:

然后:

$book=

Factory::makeBook();

All dependencies should be registered into

the factory object during run time. This object is now the

gateway that all dependencies must pass through before they can

interact with any classes. In other words, this is the

dependency container.

所有的依赖在运行期间都应在此factory对象中注册。它现在是大门,在依赖可以与任何对象打交道前,都必经此门。

The reason makeBook is a public static

function is for ease of use and global access. When I

started this article off I made a reference to the singleton

pattern and global access being a poor choices of code. They

are… for the most part. It is bad design when they control

access, but it is perfectly ok when they control creation. The makeBook function is only a shortcut for creation. There

is no dependency what-so-ever between the book class and the

factory class. The factory class exists so we can contain our

dependencies in one location and automatically inject those

dependencies with one line of code creation.

makeBook作为一个静态方法的原因是易用并且可以在全局得到。在本文开头,我提出使用singleton类型和全局变量对代码而言是一个糟糕的选择。多数情形下,确实如此!当他们控制接入时,是一个差设计。但是在他们控制创建时,是一个不错的选择。makeBook方法仅仅是创建的一个捷径。在book类和factory类之间,没有任何依赖。由于factory类的存在,我们的依赖可以存放在一个地点,并且用一行创建代码就可自动注入这些依赖。

The factory or container class removes all

of the extra work of dependency injection.

此处的factory或者容器类移走了依赖注入的所有额外工作。

Before

injection:

以前的注入:

$book=new Book();

And now:

目前的方法:

$book=

Factory::makeBook();

Hardly any extra work, but tons of extra

benefits.

几乎没有任何的额外工作,但是有大量益处。

When test code is run, specifically unit

tests, the goal is to see if a method of a class is working

correctly. Since the book class requires database access to

read the book data it adds a whole layer of complexity. The

test has to acquire a database connection, pull data, and test

it. All of a sudden the test is no longer testing a single

method in the book class, it is now also testing database. If

the database is offline, the test would fail. This is FAR

from the goal a unit test.当运行测试代码时,尤其是单元测试,其目标是检查类中的方法是否正确地工作。由于book类要求接入数据库并且读取图书数据,它增加了一层复杂度。此测试不得不进行数据库连接,取得数据,然后测试它。瞬间,测试不再是测试book类中的单个方法,它目前同时也测试数据库。如果数据库离线,测试就会失败。这已经远离了单元测试的目的。

A way of dealing with this is just using a

different database dependency for the unit tests. When the

test suite starts up a dummy database is injected into the

book. The dummy database will always have the data the

developer expects it to have. If a live database was used in

a unit test the data could potentially change causing tests to

unnecessarily fail. There is no need for a unit test to be

refactored when a record in a database changes.

解决上述问题的方法是用不同的数据库依赖来作单元测试。当测试套件开始时,虚拟的数据库被注入book.虚拟数据库将永远拥有开发人员所期待的数据。如果使用一个真实数据库,数据的潜在变化会导致测试不必要的失败。当数据库的记录改变了,不需因此重新进行单元测试。

The code is more modular because it can

dropped into any other web application. Create the book

object and inject a database connection with

$book->setDatabase(). It does not matter if

the database is in

Registery::Database, $database, or

$someRandomDatabaseVarible. As long as there is a database

connection the book will work inside any system.

此代码现在更加模块化,因为它可以被放到任何其他的网页程序。创建book对象并且用$book->setDatabase()注入数据库连接。此时,数据库在 Registery::Database,

$database,或者$someRandomDatabaseVarible都无关大局。只要有一个数据库连接,book就可以在任何系统内工作。

The code is more maintainable because each

object given exactly what it needs. If separate database

connections are required between different instances of the same class then there no

extra code needed inside the class what-so-ever. Give book1

access to database1 and book2 access to database2.

代码更加易维护,因为每个对象都被赋予了它的所需。如果同一类的实例需要不同的数据库连接,在类内部一点儿也不需要额外的代码。例如book1连接到数据库1,book2连接到数据库2

Factory::$_database=$ourDatabaseVarForDB1;

$book1=

Factory::makeBook();

$book2=

Factory::makeBook();

$book2->setDatabase($database2);

Dependency injection really is the answer

to more maintainable, testable, modular code.

依赖注入真的是更易维护,更易测试和更加模块化的答案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值