这2个其实都算得上是一种设计模式或者说是一种软件设计思想,目的都是为了增长软件可维护性和扩展性,在不少框架,好比Java Web框架SpringMVC 和PHP Web框架laravel里面都有应用。php
首先得理解什么叫依赖?从宏观上看,得益于开源软件运行的兴起,不少时候咱们写项目并非什么都是从零开始,咱们每每会利用不少现成的开源代码进行快速开发,能不重复造轮子最好,因此咱们每每依赖不少开源组件。gradle、npm、composer 等工具的部分功能就是解决项目依赖问题。laravel
从微观上看,在实际写代码里面,对象与对象之间也会产生依赖关系,好比一个数据库查询类须要用到一个数据库链接、一个文章评论类用到一个文章,这里的依赖主要指对象之间的关系。redis
举个栗子,在一个 SessionService 里面你须要一个 FileSession :数据库
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类:npm
class SessionService {
private $sessionHandler;
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类没法控制的,也就说控制权在于外部。session
不少软件在设计的时候都采用分层结构,最典型的就是计算机网络,Http协议依赖TCP协议,层与层之间经过约定的的接口进行交互,既减小了代码的复杂度,也提升了可维护性。好比说你哪一天重构了FileSession,没问题,只要你保证全部方法的返回结果和以前同样就行。composer
为了更灵活的运用这种注入机制咱们可能须要采用一个接口去约束,举个例子,咱们先增长一个接口sessionHandler:
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提供了这种可能。 一样,在laravel框架里面session自带了Cache,Database,File这种几种实现,能够随意切换。
说了最后,必须再说说IOC容器,IOC核心思想是经过IoC容器管理对象的生成、资源获取、销毁等生命周期,在IoC容器中创建对象与对象之间的依赖关系,IoC容器启动后,全部对象直接取用,调用层再也不使用new操做符产生对象和创建对象之间的依赖关系。
简单理解就是再也不使用new建立对象了,并且使用容器来管理对象,须要对象就从容器里面取,并且你只须要在参数上声明依赖,容器就直接给你对象了,炒鸡方便,好比在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容器替咱们自动注入了依赖!