Laravel 控制反转和门面模式概念详解

那多个概念对于 Laravel
的使用者来讲应该并不生分,越发是当你指望扩充只怕替换 Laravel
主题库的时候,理解和创造施用它们得以相当大进步 Laravel
的战争力。这里以创办三个和谐的 ServiceProvider 为例理解 Inversion of
Control 和 Facade 在 Laravel 中的应用。

要想通晓PHP 澳门新浦京娱乐游戏 ,信赖注入 和 调控反转 八个概念,就务须搞精晓如下的三个难点:

作者的博客最早的小说:

调控反转(Inversion of Control)

  • DI —— Dependency Injection 正视注入
  • IoC —— Inversion of Control 调控反转

laravel 提供了四个心闲手敏的格局,那正是 facade 。框架之中的 DB、Auth、File
等成效也是有相关的 facade 实现。那么,该怎么样写自身的 facade 呢?

什么是 IoC

支配反转(Inversion of
Control,缩写为IoC),是面向对象编制程序中的一种设计规范,能够用来减低计算机代码之间的耦合度。在那之中最见死不救的不二等秘书诀叫做信任注入(Dependency
Injection,简单称谓DI),还大概有一种方法叫“正视查找”(Dependency
Lookup)。通过垄断(monopoly卡塔尔反转,对象在被创建的时候,由二个调节连串内享有目的的外部实体,将其所重视的靶子的援引传递给它。
— 维基百科

简言之说来,便是八个类把自个儿的的调节权交给其余三个目的,类间的信赖由那几个目的去化解。依赖注入归于信赖的来得表明,而依附于查找则是因而查找来减轻信任。

如何是借助注入

一向不您笔者就活不下去,那么,你就是本身的依据。 说白了正是:

不是本身自身的,却是小编索要的,皆以自个儿所注重的。一切须要外表提供的,都是索要张开正视注入的。

第一,facade 并不是 laravel
独有的事物,它便是设计格局中的外观方式。当然,这里就不当机不断去商讨外观方式的定义了。那篇文章写的十分不错
: 设计形式外观情势Facade。那么,laravel 的 facade 做了哪些?同样的,
laravel 达成了外观方式的开关作用,何况应用魔术点子 __callstatic
实现了静态格局调用、动态创制对象的意义。参照他事他说加以考察

Laravel 中的使用

流入二个类:

App::bind('foo', function($app)
{
    return new FooBar;
});

本条例子的情趣是创制三个小名字为 foo 的类,使用时实际实例化的是 FooBar

使用那些类的法子是:

$value = App::make('foo');

$value 实际上是 FooBar 对象。

一经愿意选取单例形式来实例化类,那么使用:

App::singleton('foo', function()
{
    return new FooBar;
});

那样的话每回实例化后的都以同三个目的。

流入类的更加多例子能够看 Laravel 官网

您可能会疑窦下面的代码应该写在哪个地方呢?答案是你希望她们在什么地方运转就写在哪里。0
—— 0 知道写哪个地方还用来看这种根基随笔么!

依附注入比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Boy {
  protected $girl;
 
  public function __construct(Girl $girl) {
    $this->girl = $girl;
  }
}
 
class Girl {
  ...
}
 
$boy new Boy();  // Error; Boy must have girlfriend!
 
// 所以,必须要给他一个女朋友才行
$girl new Girl();
 
$boy new Boy($girl); // Right! So Happy!

从上述代码大家得以看来Boy强依赖Girl必须要在布局时注入Girl的实例才行。

那正是说为何要有依赖注入本条概念,依赖注入到底解决了怎样难题?

我们将上述代码校正一下大家初学时都写过的代码:

1
2
3
4
5
6
7
class Boy {
  protected $girl;
 
  public function __construct() {
    $this->girl = new Girl();
  }
}

这种艺术与前方的措施有啥样差别吧?

笔者们会发觉Boy的女对象被大家硬编码到Boy的人身里去了。。。
每趟Boy重生本身想换个类其余女对象都要把温馨扒光才行。

某天Boy特意爱怜二个LoliGirl ,特别想让他做自个儿的女对象。。。如何做?
重生自身。。。扒开自身。。。把Girl扔了。。。把 LoliGirl塞进去。。。

1
2
3
4
5
6
7
8
9
10
11
12
class LoliGirl {
 
}
 
class Boy {
  protected $girl;
 
  public function __construct() {
      //  $this->girl = new Girl();  // sorry...
      $this->girl = new LoliGirl();
  }
}

某天 Boy鬼摸脑壳上了水晶室女….Boy 好烦。。。

是或不是感到不太好?每一回境遇真心对待的人却要那样的劫难本人。。。

Boy说,作者要变的精锐一点。小编不想被改来改去的!

好吧,我们让Boy无敌一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Girl {
  // Boy need knows that I have some abilities.
}
 
class LoliGril implement Girl {
  // I will implement Girl's abilities.
}
 
class Vixen implement Girl {
  // Vixen definitely is a girl, do not doubt it.
}
 
class Boy {
  protected $girl;
 
  public function __construct(Girl $girl) {
    $this->girl = $girl;
  }
}
 
$loliGirl new LoliGirl();
$vixen new Vixen();
 
$boy new Boy($loliGirl);
$boy new Boy($vixen);

Boy很开心,终于得以毫不扒开自个儿就能够体验分化的人生了。。。So Happy!

自然你可能感到这么些概念很空虚,都怎么东西。那么实际上综上可得,laravel 的
facade 就是将或多或少职能封装成工具类,并且能以静态情势调用工具类的点子。

劳务提供器 (Service Providers卡塔尔

为了让依赖注入的代码不至于写乱,Laravel 搞了三个 服务提供器(ServiceProvider)的东西,它将那一个注重聚焦在了一块,统一申明和拘押,让正视变得愈加便于保险。

依赖注入格局

1、构造器 注入

1
2
3
4
5
6
7
8
<?php
class Book {
  private $db_conn;
  
  public function __construct($db_conn) {
    $this->db_conn = $db_conn;
  }
}

2、setter 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class Book {
    private $db;
    private $file;
 
    function setdb($db) {
        $this->db = $db;
    }
 
    function setfile($file) {
        $this->file = $file;
    }
}
 
class file {
}
 
class db {
}
 
// ...
 
class test {
    $book new Book();
    $book->setdb(new db());
    $book->setfile(new file());
}

小结:

因为超越三分之一应用程序都是由多少个或许越来越多的类经过相互同盟来促成职业逻辑,那使得各样对象都亟需得到与其同盟的对象(相当于它所依赖的目的)的援用。要是那么些得到进度要靠本人达成,那么将引致代码中度耦合并且难以维护和调弄收拾。

就此才有了凭仗注入的定义,注重注入撤消了以下难题:

  • 依据于之间的解耦
  • 单元测量试验,方便Mock

上边俩种艺术代码很清楚,然而当我们供给注入比相当多个依据时,意味着又要扩充超多行,会比较为难管理。

正如好的解决办法是
创立二个class作为颇负正视关系的container,在这里个class中可以贮存、创制、获取、查找供给的信赖关系。先来打听一下IOC的概念

首先、以 laravel 5.1 框架,笔者早前写过的 Geoip facade
为例,说一下怎么去创设友好的 facade。

Laravel 中的使用

概念四个服务提供器:

use IlluminateSupportServiceProvider;

class FooServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind('foo', function()
        {
            return new Foo;
        });
    }

}

其一代码也简单明白,正是说Bellamy(Bellamy卡塔尔(قطر‎个服务提供器,那么些服务提供器有贰个 register的点子。那几个办法达成了小编们地点讲到的注重注入。

当我们实践下边代码:

App::register('FooServiceProvider');

我们就完毕多少个注入了。不过那几个照旧快心满志动写,所以怎么让 Laravel
本身来做那事情啊?

大家只要在 app/config/app.php 中的 providers 数组里面扩充一行:

'providers' => [
    …
       ‘FooServiceProvider’,
],

那般大家就足以应用 App::make(‘foo’) 来实例化一个类了。

你忍不住要问了,这么写也太丢人了啊?莫慌,有法子。

决定反转 (Inversion Of Control, IOC)

支配反转 是面向对象编制程序中的一种设计原则,能够用来减低Computer代码之间的耦合度。在这之中最广泛的方法叫做 依靠于注入(Dependency
Injection, DI), 还恐怕有一种叫”正视查找”(Dependency
Lookup)。通过调控反转,对象在被创设的时候,由一个调整种类内享有指标的外面实体,将其所重视的指标的援引传递给它。也足以说,信任被注入到对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
 
class Ioc {
    protected $db_conn;
 
    public static function make_book() {
        $new_book new Book();
        $new_book->set_db(self::$db_conn);
        //...
        //...
        //其他的依赖注入
        return $new_book;
    }
}

这时候,假如取得三个book实例,只须求履行$newone = Ioc::makebook(卡塔尔国;

上述是container的多个切实可行实例,最佳如故不要把实际的某些正视注入写成方法,接纳registry注册,get获取比较好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
/**
 * 控制反转类
 */
class Ioc {
    /**
     * @var array 注册的依赖数组
     */
    protected static $registry array();
 
    /**
     * 添加一个 resolve (匿名函数)到 registry 数组中
     *
     * @param string  $name    依赖标识
     * @param Closure $resolve 一个匿名函数,用来创建实例
     * @return void
     */
    public static function register($name, Closure $resolve) {
        static::$registry[$name] = $resolve;
    }
 
    /**
     * 返回一个实例
     *
     * @param string $name 依赖的标识
     * @return mixed
     * @throws Exception
     */
    public static function resolve($name) {
        if (static::registered($name)) {
            $name static::$registry[$name];
            return $name();
        }
 
        throw new Exception("Nothing registered with that name");
    }
 
    /**
     * 查询某个依赖实例是否存在
     *
     * @param string $name
     * @return bool
     */
    public static function registered($name) {
        return array_key_exists($namestatic::$registry);
    }
}

这段时间就足以因而如下方式来注册和注入叁个

1
2
3
4
5
6
7
8
9
10
11
<?php
Ioc::register("book"function () {
    $book new Book();
    $book->setdb('db');
    $book->setfile('file');
 
    return $book;
});
 
// 注入依赖
$book = Ioc::resolve('book');

下载 geoip 扩展

geoip 是二个方可更具 IP 获取国家、地域、城市消息的 PHP 扩展,基于
maxmind 数据库。 github 在这里。

首先,为 laravel 添加 geoip 扩展。打开 composer.json,添加
“geoip2/geoip2”: “~2.0” 到 require。项目根目录运行 composer update (
须要设置 composer 卡塔尔国更新一下,geoip 的借助和软件包就被下载到 vendor
文件夹中了。

然后下载 geoip 信任的数据库,免费库的地点 : GeoLite2

我下载了 吉优Lite2 Country 和 GeoLite2 City 库,放到了 storage/geoipdb
中。

伪装情势(Facade)

为了让 Laravel 中的核心类使用起来越发惠及,Laravel实现了伪装方式。

外觀情势(Facade
pattern),是軟件工程中常用的一種軟件設計方式,它為子系統中的一組接口提供一個統一的高層接口,使得子系統更便于选择。
— 维基百科

难题汇总

建立 facade。

在 app 目录下新建 Facades 文件夹,里面新建 Facades/GeoIP/GeoIP.php 和
Facades/GeoIP/Facade/GeoIP.php
(提出每一个作用新建三个文件夹区分,举例小编那边给 GeoIP
新建三个文书夹,关于吉优IP 的事物全放到这里卡塔尔(قطر‎注意,Facades/GeoIP 下的
GeoIP.php 是您要对 geoip 扩大实行打包的类, Facades/吉优IP/Facade 下的
GeoIP.php 是你的 facade,用来给 laravel 剖析选择,那多少个公文能够分裂名。

目录构造如图:

澳门新浦京娱乐游戏 1

Facades/GeoIP/Facade/GeoIP.php 如下

<?phpnamespace AppFacadesGeoIPFacade;use IlluminateSupportFacadesFacade;class GeoIP extends Facade{ protected static function getFacadeAccessor() { return 'geoip'; }}

小心你的 facade 今后只有一个主意,重临了一个字符串 ‘geoip’ ,
那么些字符串是二个注脚,用来给 laravel 的劳动提供者剖判选拔的。

Facades/GeoIP/GeoIP.php 如下(戏弄:写的有一点随便)

<?phpnamespace AppFacadesGeoIP;use GeoIp2DatabaseReader;class GeoIP{ /** * GeoIP country db path (base on storage_path). * * @var GeoIP */ private $_country_db = 'geoipdb/GeoLite2-Country.mmdb'; /** * GeoIP city db path (base on storage_path). * * @var GeoIP */ private $_city_db = 'geoipdb/GeoLite2-City.mmdb'; /** * Instance for GeoIP . * * @var GeoIP */ private $_instance; /** * Init instance. * */ public function init { switch  { case 'getCountry': $path = $this->_country_db; break; case 'getCity': $path = $this->_city_db; break; default: break; } $this->_instance = new Reader(storage_path; } /** * Get Country infomations. * * @param String $ip * @return Array */ public function getCountry { $this->init(__FUNCTION__); $record = $this->_instance->country; // 国家信息 $data['iso_code'] = $record->country->isoCode; $data['country_name'] = $record->country->name; $data['country_name_zh_cn'] = $record->country->names['zh-CN']; return $data; } /** * Get City infomations. * * @param String $ip * @return Array */ public function getCity { $this->init(__FUNCTION__); $record = $this->_instance->city; $data['iso_code'] = $record->country->isoCode; $data['country_name'] = $record->country->name; $data['country_name_zh_cn'] = $record->country->names['zh-CN']; // 省、州信息 $data['sub_division_name'] = $record->mostSpecificSubdivision->name; $data['sub_division_name_zh_cn'] = $record->mostSpecificSubdivision->names['zh-CN']; $data['sub_division_code'] = $record->mostSpecificSubdivision->isoCode; // 城市信息 $data['city_name'] = $record->city->name; $data['postal_code'] = $record->postal->code; // 经纬度 $data['latitude'] = $record->location->latitude; $data['longitude'] = $record->location->longitude; return $data; }}

OK,今后 geoip 的常用成效已经封装到点子中了。

成功了 facade 的创造和意义封装,上边就要动用它了。本人创办的 facade 要在
laravel 使用是要扩充挂号的,以便 laraval 在运行时能自动注入依赖(请看
laravel 的依据注入简单介绍 : laravel 信任注入 高校君State of Qatar

Laravel 中的使用

小编们使用的超过四分之十五宗旨类都以依靠门面方式完结的。举个例子:

$value = Cache::get('key');

那几个静态调用实际上调用的并非静态方法,而是经过 PHP 的魔术点子__callStatic() 讲哀求转到了对应的章程上。

那么哪些讲大家前段时间写的服务提供器也如此使用呢?方法非常粗大略,只要这么写:

use IlluminateSupportFacadesFacade;

class Foo extends Facade {

    protected static function getFacadeAccessor() { return ‘foo’; }

}

那般我们就足以因此 Foo::test() 来调用大家早前真的的 FooBar 类的措施了。

1、参预者都有哪个人?

答:诚如有三方到场者,一个是某些对象;二个是IoC/DI的容器;另三个是有个别对象的外部能源。又要名词解释一下,有些对象指的就是大肆的、普通的Java对象;
IoC/DI的器皿简单题说正是指用来达成IoC/DI功用的一个框架程序;对象的外界财富指的即是目的必要的,可是是从对象外部得到的,都统称资源,比方:对象急需的别的对象、只怕是目的须求的文书财富等等。

编战胜务提供者

在 app/Providers 下新建 FacadesServiceProvider.php能够手动建,也得以用
artisan 命令来扭转,随你喜爱。app/Providers/Facades瑟维斯Provider.php
代码如下:

<?phpnamespace AppProviders;use AppServiceApiService;use IlluminateSupportServiceProvider;// include the class facade bindeduse AppFacadesGeoIPGeoIP;class FacadesServiceProvider extends ServiceProvider{ /** * 在容器中注册绑定。 * * @return void */ public function register() { $this->app->singleton('geoip', function  { return new GeoIP; }}

地方代码可以预知,服务提供者注册时会注册贰个单例,标号为
‘geoip’,也便是我们团结的 facade
重临的十三分,然后回调函数会回去二个对象,约等于我们封装 geoip
作用的不胜类的实例,不亮堂的同室可以看看 laravel
的服务提供者和劳务容器相关知识哦。(注意要 use 将 facade
和封装类的命名空间援用一下啊卡塔尔

别名(Alias)

神跡大家恐怕将 Facade 放在大家扩展库中,它有相比较深的命名空间,如:LibraryMyClassFoo。那样变成使用起来并不平价。Laravel
能够用小名来替换掉那样长的名字。

我们借使在 app/config/app.php 中 aliases 下扩充一行就能够:

'aliases' => [
    …
    'Foo' => ‘LibraryMyClassFoo’,
],

这么它的应用就由 LibraryMyClassFoo::test() 变成 Foo::test() 了。

2、重视:什么人正视于何人?为何会有依据?

答:某些对象信任于IoC/DI的容器。依赖是不可翻盘的,在贰个品类中,种种类之间有多样三种的涉嫌,不或许整个全然独立,那就形成了依据。守旧的支出是运用别的类时直接调用,那会形成强耦合,那是要防止的。依赖注入借用容器转移了被重视对象实现解耦。

注册服务提供者

laravel 5.1 以上版本的话, config/app.php 中找到 providers 和 aliases
,将您的劳务提供者和 facade 小名配置一下 :

providers 加入 :

AppProvidersFacadeServiceProvider::class,

aliases 参与(不用每一趟都写非常短的命名空间前缀卡塔尔国 :

'GeoIP' => AppFacadesGeoIPFacadeGeoIP::class,

对于 lumen 5.2 以上,需要在 bootstrap/app.php 中添加

$app->register(AppProvidersFacadesServiceProvider::class);

注册结束后,每一趟使用 facade::function 的时候,laravel 会自动解析facade, 然后创立贰个指标给顾客使用,,而不供给客商自个儿去 new
一个目的出来。

当今,在其他三个调节器,大概路由的回调函数中,使用

$res = GeoIP::getCountry('75.101.195.215');var_dump;

您会意识,facade 已经能够优秀工作了,enjoy!

外观情势FacadeLaravel 服务容器实例教程 ——
深刻理解调节反转和信任注入Laravel 服务提供者实例教程 —— 创设 ServiceProvider 测验实例

总结

从而有了决定反转(Inversion of
Control)和门面情势(Facade),实际还可能有服务提供器(ServiceProviders)和小名(Alias),我们创设和睦的类库和扩充 Laravel
都会有益于广大。

此地计算一下创制自个儿类库的法子:

  1. 在 app/library/MyFoo 下创制类 MyFoo.php
  2. 在 app/library/MyFoo/providers 下创建 MyFooServiceProvider.php
  3. 在 app/library/MyFoo/facades 下创建 MyFooFacade.php
  4. 在 app/config/app.php 中添加 providers 和 aliases

3、注入:何人注入于谁?到底注入什么?

答:透过容器向目的明入其所急需的外表能源

4、调控反转:哪个人说了算什么人?调控什么?为何叫反转?

答:IoC/DI的器皿调节目的,主若是决定目的实例的始建。反转是周旋张巍向来讲的,那么怎么着算是正向的吗?构思一下常规状态下的应用程序,假若要在A里面使用C,你会如何做吧?当然是直接去创设C的靶子,也便是说,是在A类中金石不渝去获得所要求的表面能源C,这种情景被可以称作正向的。那么怎么着是反向呢?就是A类不再主动去赢得C,而是悲伤等待,等待IoC/DI的容器获取二个C的实例,然后反向的流入到A类中。

5、正视注入和决定反转是千人一面概念吗?

答:从地点能够见见:重视注入是从应用程序的角度在汇报,能够把注重注入描述完整点:应用程序依赖容器创设并流入它所急需的表面能源;而决定反转是从容器的角度在陈诉,描述完整点:容器调控应用程序,由容器反向的向应用程序注入应用程序所供给的外界能源。

 

 

参考:

Laravel 中的 信任注入 与
调节反转

发表评论

电子邮件地址不会被公开。 必填项已用*标注