资讯专栏INFORMATION COLUMN

用trait实现简单的依赖注入

LeviDing / 2365人阅读

摘要:在本篇,我尝试用另一种方式实现。自动注入可以简单理解为可以复用的方法,下面来看看怎么用来实现自动注入。

这里先假设一个场景:

有一个工厂(Factory),需要工人(Worker) 使用某个机器(Machine)来生产某种产品

即有这样的依赖关系: Factory --> Worker --> Machine

不使用注入

代码大概是这样子:

class Machine{
    function run(){
        echo "机器开动";
    }
}
class Worker {
    function work(){
        echo "工人开动机器 -> ";
        $machine = new Machine();
        $machine -> run();
    }
}
class Factory{
    function produce(){
        echo "工厂叫工人开工 -> ";
        $worker = new Worker();
        $worker -> work();
    }
}
$factory = new Factory();
$factory -> produce();

可以看出来,这里所依赖的对象都由类自己在内部实例化,是一种强耦合,不利于测试和维护。比如我现在要改成另一种工人来生产,那就要改工厂内部,这是不合理的。

手工注入

所谓的注入,就是将类所依赖的对象的实例化操作放在类外面,同时使用Interface来作出约束:

Interface Machine{
    public function run();
}
Interface Worker{
    public function work();
}
class Machine1 implements Machine{
    function run(){
        echo "机器 1 开动";
    }
}
class Machine2 implements Machine{
    function run(){
        echo "机器 2 开动";
    }
}

class Worker1 implements Worker{
    private $machine;
    public function __construct(Machine $machine){
        $this -> machine = $machine;
    }
    function work(){
        echo "工人 1 开动机器 -> ";
        $this -> machine -> run();
    }
}
class Worker2 implements Worker{
    private $machine;
    public function __construct(Machine $machine){
        $this -> machine = $machine;
    }
    function work(){
        echo "工人 2 开动机器 -> ";
        $this -> machine -> run();
    }
}

class Factory{
    private $worker;
    public function __construct(Worker $worker){
        $this -> worker = $worker;
    }
    function produce(){
        echo "工厂叫工人开工 -> ";
        $this -> worker -> work();
    }
}

$machine = new Machine1();
$worker = new Worker2($machine);
$factory = new Factory($worker);

$factory -> produce();

可以看出来,这样的好处是解耦。比如:可以由工人1开动机器2来生产,也可以由工人2来开动机器1来生产,以后也可以随时用新的工人(只要他会work)、Worker也可以随时换其它的机器(只要它会run)来生产。这种转换都不需要修改工厂或工人的代码。

那么问题来了,现在只是把实例化从里面移到了外面,但如果依赖的东西多了,也是很麻烦的,这就需要一个自动注入的机制,也就是平时经常听到的注入容器,常见的容器都是用到反射来实现,而且要在构造函数中声明注入的类型,相对还是比较麻烦。

在本篇,我尝试用另一种方式实现。

trait自动注入

trait可以简单理解为可以复用的方法,下面来看看怎么用trait来实现自动注入。

思路就是用trait来实现魔术方法__get,通过该方法来自动生成依赖的对象,先看完整代码

trait DI{
    private $map = ["Worker" => "Worker1","Machine"=>"Machine2"];

    function __get($k){
        if(preg_match("/^[A-Z]/", $k)) {
            $obj =  new $this -> map[$k];
            if($obj instanceof $k){
                return $obj;
            }else{
                exit("不符约定");
            }
        }
    }
}

Interface Machine{
    public function run();
}
Interface Worker{
    public function work();
}
class Machine1 implements Machine{
    function run(){
        echo "机器 1 开动";
    }
}
class Machine2 implements Machine{
    function run(){
        echo "机器 2 开动";
    }
}
class Worker1 implements Worker{
    use DI;
    function work(){
        echo "工人 1 开动机器 -> ";
        $this -> Machine -> run();
    }
}
class Worker2 implements Worker{
    use DI;
    function work(){
        echo "工人 2 开动机器 -> ";
        $this -> Machine -> run();
    }
}
class Factory{
    use DI;
    function produce(){
        echo "工厂叫工人开工 -> ";
        $this -> Worker -> work();
    }
}

$factory = new Factory();
$factory -> produce();

trait中的map用来演示实现类和接口的绑定关系,以便进行类型约束,实际应用时可以用配置或其它方式实现.

类中要使用依赖注入时,需声明use DI, 同时,注入的对象为首字母大写(你也可以用其它规则,相应的修改trait中的判断)

当然了,这只是一个很粗糙的演示,只实现了基本的自动注入,它还有很多问题,比如原来的类如果也有__get方法时,就会产生覆盖。

有兴趣的可以尝试完善一下看能不能在项目中实际使用。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28396.html

相关文章

  • 【Laravel】Laravel 框架关键技术解析·读书笔记(一)

    摘要:判断是否存在构造函数,不存在直接实例化,存在则通过来获取输入函数,并有相应的方法解决依赖参数问题,实现依赖注入。 Laravel 框架关键技术解析·读书笔记(一) 第一章 入口文件 请求访问的入口文件,主要完成几部分工作,分别是: 自动加载函数的添加 服务器实例化与服务注册 路由加载 请求实例化与路由分发 相应生成与发送 其中,自动加载函数用于包含引用文件,改文件是composer...

    CocoaChina 评论0 收藏0
  • PHP 特性之 trait (一)

    摘要:在阅读文档的时候看到一个类是以声明的。是一种代码复用技术,为的单继承限制提供了一套灵活的代码复用机制。下面把的代码做一个修改以上代码会输出。可以看出优先级的顺序为,当前类的优先级基类。   在阅读laravel文档的时候看到一个类 是以 Trait声明的。平时在工作中从来没有用过,于是就去看了下官方文档。原来这个类早在php5.4.0就有了。可惜在项目中从来没有用过。   Trait看...

    ermaoL 评论0 收藏0
  • 简述我所理解 PHP Trait

    摘要:和组合的语义定义了一种减少复杂性的方式,避免传统多继承和类相关典型问题。队列的目的是将耗时的任务延时处理,比如发送邮件,从而大幅度缩短请求和相应的时间。同样的道理,根据引入不同的来完成对应的功能。 showImg(https://segmentfault.com/img/remote/1460000010868178); Trait 概念 在常规的 PHP 开发中,我们都习惯于先编写一...

    gecko23 评论0 收藏0
  • [译] Laravel-Excel 3.0 文档

    摘要:事件将通过添加关注来激活。自动注册事件监听器通过使用,你可以自动注册事件监听器,而不需要使用。你可以自由使用这个宏,或者创造你自己的语法以上例子可作对于方法可查看文档测试测试下载测试存储导出测试队列导出 Basics 最简单的导出方法是创建一个自定义的导出类, 这里我们使用发票导出作为示例. 在 App/Exports 下创建一个 InvoicesExport 类 namespace...

    canopus4u 评论0 收藏0
  • 什么是Trait(性状)?

    摘要:很多开发朋友都没有弄清楚性状。性状有两个作用表明类可以做什么像是接口提供模块化实现像是类。注是不要自我重复的简称,表示不要在多个地方重复编写相同的代码,如果需要修改遵守这个原则编写的代码,只需要在一出修改,改动就能体现到其他地方。 很多PHP开发朋友都没有弄清楚Trait(性状)。这是PHP5.4.0引入的新概念,既像类又像接口。性状是类的补分实现(即常量、属性、方法),可以混入一个...

    sushi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<