从百草园到三味书屋 (一)依赖注入
何为依赖注入?
简单分析如下代码,是否存在问题?
class UserController extends BaseController{
public function getIndex() {
$users = User::all();
return View::make('users.index', compact('users'));
}
}
分析:
这段代码很 简短,但我们要想测试这段代码的话就一定会和实际的数据库发生联系。
也就是说, Eloquent ORM(译者注:Laravel的数据库对象模型库)和该控制器有着紧耦合。如果不使用Eloquent ORM,不连接到实际数据库,我们就没办法运行或者测试这段代码。这段代码同时也违背了“关注分离”这个软件设计原则。
简单讲:这个控制器知道的太多了。 控制器不需要去了解数据是从哪儿来的,只要知道如何访问就行。控制器也不需要知道这数据是从MySQL或哪儿来的,只需要知道这数据目前是可用的。
Separation Of Concerns 关注分离
每一个类都应该有单独的职责,并且该职责应完全被这个类封装。
Build A Contract 建立约定
//定义接口
interface UserRepositoryInterface
{
public function all();
}
//实现接口
class DbUserRepository implements UserRepositoryInterface
{
public function all()
{
return User::all()->toArray();
}
}
// 面向接口编程,通过构造函数,注入实例(实现接口的任意类实例)
class UserController extends BaseController
{
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
public function getIndex()
{
$users=$this->users->all();
return View::make('users.index', compact('users'));
}
}
分析:
现在我们的控制器就完全和数据层面无关了。在这里无知是福!
我们的数据可能来自MySQL,MongoDB或者Redis。我们的控制器不知道也不需要知道他们的区别。
仅仅做出了这么小小的改变,我们就可以独立于数据层来测试Web层了,将来切换存储实现也会很容易。
Respect Boundaries 严守边界
记得要保持清晰的责任边界。 控制器和路由是作为HTTP和你的应用程序之间的中间件来用的。当编写大型应用程序时,不要将你的领域逻辑混杂在其中(控制器、路由)。
示例:
场景:提醒用户交钱,需用到支付 和 通知 两个方法。
//定义账单支付接口
interface BillerInterface {
public function bill(array $user, $amount);
}
//定义账单通知接口
interface BillingNotifierInterface {
public function notify(array $user, $amount);
}
//实现账单支付接口,同时注入,账单通知接口
class StripeBiller implements BillerInterface{
public function __construct(BillingNotifierInterface $notifier)
{
$this->notifier = $notifier;
}
public function bill(array $user, $amount)
{
$this->notifier->notify($user, $amount);
}
}
//使用sms作为账单支付通知,那我们如何做依赖注入呢?很简单:
$biller = new StripeBiller(new SmsNotifier);
分析:
这就是依赖注入。(与容器ioc可以是脱离独立存在的,后面会讲解ioc)
biller不需再考虑提醒用户的事儿,我们直接传给他一个提示器(notifier)。 这种微小的改动能使你的应用焕然一新。
你的代码马上就变得更容易维护, 因为明确指定了类的职责边界。 并且更容易测试, 你只需使用模拟依赖即可。
依赖注入核心
依赖注入的核心,是基于接口编程,实际目的在于解耦,使得类之间更加独立,健壮,易于维护。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: