闽公网安备 35020302035485号
这2个其实都算得上是一种设计模式或者说是一种软件设计思想,目的都是为了增加软件可维护性和扩展性,比如在Java Web框架SpringMVC 和PHP Web框架laravel里面都有应用。
首先得理解什么叫依赖?从宏观上看,得益于开源软件运行的兴起,很多时候我们写项目并不是什么都是从零开始,我们往往会利用很多现成的开源代码进行快速开发,能不重复造轮子最好,所以我们往往依赖很多开源组件。gradle、npm、composer 等工具的部分功能就是解决项目依赖问题。
class FileSession
{
private $file;
... more code
public function set($name, $value)
{
echo "set $name = $value into $this->file\n";
}
public function get($name)
{
echo "get $name value\n";
}
}
service类:class SessionService
{
private $sessionHandler;
// 堆代码 duidaima.com
public function __construct()
{
$this->sessionHandler = new FileSession();
}
public function set($name, $value)
{
$this->sessionHandler->set($name, $value);
}
public function get($name)
{
return $this->sessionHandler->get($name);
}
...more code
}
在这种普通写法里面,当我们需要一个 sessionHandler 的时候我们是直接在构造函数里面实例化,这样没啥问题,确实解决了依赖问题。但是依赖注入的另一个词“注入”更强调的是一种从外部而来的,而不是内部。class SessionService
{
private $sessionHandler;
public function __construct($sessionHandler)
{
$this->sessionHandler = $sessionHandler;
}
public function set($name, $value)
{
$this->sessionHandler->set($name, $value);
}
public function get($name)
{
return $this->sessionHandler->get($name);
}
...more code
}
这种写法要求你在使用service的时候从外部传入一个handler,这就实现了依赖注入,注入的方式有很多种,刚才这种可以称之为构造器注入,还有一种叫setter注入,比如说,我们可以在service里面里面提供一个setter函数用于设置所需的handler:public function setSessionHandler($sessionHandler)
{
$this->sessionHandler = $sessionHandler
}
这种写法有哪些好处呢?一个是解耦,假如说这个FileSession实例化的时候还需要其它操作,比如传入一个配置参数,原本的写法可能就需要更改service类了,在构造函数里面啪啪啪写一堆。还有就是方便测试,既然解耦了就可以很方便的进行单元测试。另一个是控制反转,就是说这个FileSession外部传入的,是service类无法控制的,也就说控制权在于外部。interface SessionHandler
{
public function set($name, $value);
public function get($name);
}
我们约定,只要你实现了这个接口,你就可以当一个sessionHandler,你就可以用来处理session,至于你怎么实现,service不管,比如说我们换一个redis:class RedisHandler implments SessionHandler
{
private $redisInstance;
public function __construct()
{
$this->redisInstance = new Redis();
}
public function set($name, $value)
{
$this->redisInstance->set($name, $value);
}
public function get($name)
{
return $this->redisInstance->get($name);
}
}
这时候我们可以在service的构造函数稍作修改,增加一个类型约束:public function __construct(SessionHandler $sessionHandler)
{
$this->sessionHandler = $sessionHandler;
}
这样的设计之后,好处显而易见,我们可以很轻松替换掉之前的fileSession,不改动service的一行代码,只要按照sessionHandler的接口去实现相应的方法就行,在laravel里面这样的接口就叫做 Contracts,下面就是框架里面的Cache缓存的 Contracts:<?php
namespace Illuminate\Contracts\Cache;
interface Store
{
/**
* Retrieve an item from the cache by key.
*
* @param string|array $key
* @return mixed
*/
public function get($key);
/**
* Retrieve multiple items from the cache by key.
*
* Items not found in the cache will have a null value.
*
* @param array $keys
* @return array
*/
public function many(array $keys);
/**
* Store an item in the cache for a given number of minutes.
*
* @param string $key
* @param mixed $value
* @param float|int $minutes
* @return void
*/
public function put($key, $value, $minutes);
/**
* Store multiple items in the cache for a given number of minutes.
*
* @param array $values
* @param float|int $minutes
* @return void
*/
public function putMany(array $values, $minutes);
... more code
}
据我看到的,在laravel框架里面自带了至少5种实现,分别是Array、File、Database、Memcached、Redis, 如果你愿意你也可以自己去实现这个 Contracts,然后替换到框架里面的,不过框架本身实现的已经非常优秀了,除非你写的更好,一般情况下不需要这样做,但是laravel提供了这种可能。public function comment(Post $post, Request $request)
{
$this->validate($request, [
'content' => 'required|min:5'
]);
$comment = new Comment([
'content' => $request->get('content'),
'user_id' => auth()->user()->id,
'post_id' => $post->id,
]);
$post->comments()->save($comment);
return redirect()->back();
}
我们只需要在方法的参数上面标明所需的方法,就可以在代码直接用了,ioc容器替我们自动注入了依赖!