声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。
欢迎转载,转载请注明出处,谢谢!
依赖反转原则
介绍
我们来到了SOLID设计原则的最终的目标远景!它就是依赖反转原则,它是指高阶代码不能依赖低阶代码。相应的,高阶代码应该依赖一个抽象层,它是在高阶代码和低阶代码之间的“中间人”角色。另一方面,该原则指代抽象层不依赖具体实现,而是细节依赖抽象。如果这个读起来很晦涩,别担心。我们下面会对这两个方面具体的阐述本原则。
依赖反转原则 本原则是指高阶代码不依赖低阶代码,抽象不依赖具体细节。
实探
如果你已经读过本书之前的章节,就应该对依赖反转有一个很好的理解。我们通过下面例子来解释:
class Authenticator {
public function __construct(DatabaseConnection $db)
{
$this->db = $db;
}
public function findUser($id)
{
return $this->db->exec('select * from users where id = ?', array($id));
}
public function authenticate($credentials)
{
// Authenticate the user...
}
}
可以猜到,Authenticator
类是负责查找并验证用户的。我们来检验下类的构造器。可以看到我们有个链接数据库的实例DatabaseConnection
。所以我们将验证器和数据库紧密的接合在一起了,这意味着用户对象必须建立在关系型数据库查询之上。此外,我们的高阶代码(Authenticator
类)直接依赖了低阶代码(DatabaseConnection
类)。 首先,我们解释下“高阶”和“低阶”代码。低阶代码实现像这种磁盘文件访问,数据库接入等。高阶代码在低阶代码之上实现逻辑功能的封装,但不能将他们耦合进来。或者,高阶代码依赖建立在低阶代码之上的抽象层,如接口。不仅如此,低阶代码_也_依赖于抽象层。我们来实现一个可以在Authenticator
类中使用的接口:
interface UserProviderInterface {
public function find($id);
public function findByUsername($username);
}
然后,将接口的实现注入到Authenticator
:
class Authenticator {
public function __construct(UserProviderInterface $users,
HasherInterface $hash)
{
$this->hash = $hash;
$this->users = $users;
}
public function findUser($id)
{
return $this->users->find($id);
}
public function authenticate($credentials)
{
$user = $this->users->findByUsername($credentials['username']);
return $this->hash->make($credentials['password']) == $user->password;
}
}
这些改变之后,我们的Authenticator
现在依赖两个高阶抽象:UserProviderInterface
和HasherInterface
。我们就能自由的将任何针对接口的实现注入到Authenticator
中了。比如,如果我们用户存储在Reids中,可以实现针对UserProvider
实现一个RedisUserProvider
类。Authenticator
现在不在直接依赖低阶的存储操作了。 此外,自从它实现接口本身后,我们的低阶代码现在也是依赖高阶的UserProviderInterface
抽象:
class RedisUserProvider implements UserProviderInterface {
public function __construct(RedisConnection $redis)
{
$this->redis = $redis;
}
public function find($id)
{
$this->redis->get('users:'.$id);
}
public function findByUsername($username)
{
$id = $this->redis->get('user:id:'.$username);
return $this->find($id);
}
}
反转思想 很多开发人员在应用中使用_反转_原则。代替这种高阶直接耦合低阶代码的“自上而下”的方式,本原则指高阶、低阶代码“同时”依赖一个高阶抽象层。
在我们将Authenticator
的依赖“倒置”前,他是无法在其他数据存储系统中使用的。在改变存储系统的情况下,必须对Authenticator
进行修改,违背了开放封闭原则。我们已经知道,几种原则之间是相互贯穿的。 在将Authenticator
强制实现在存储层之上的抽象层,我们可以根据UserProviderInterface
接口约定切换成任意其他存储系统,而无需对Authenticator
本身进行修改。传统的依赖痛过“倒置”就能事代码变得非常灵活,易于改变!