文档章节

依赖反转 和 依赖注入 (PHP)

dubox
 dubox
发布于 2017/11/25 02:48
字数 1111
阅读 25
收藏 0

读了文章 《深入探討依賴注入 》做下笔记和总结

文章主要说了这样一个思想进阶过程:

传统实现 —— Interface(依赖反转) —— 工廠模式 —— Constructor Injection(依赖注入) —— Method Injection

 

1.传统实现

class ShippingService
{
    /**
     * @param string $companyName
     * @param int $weight
     * @return int
     * @throws Exception
     */
    public function calculateFee($companyName, $weight)
    {
        if ($companyName == 'BlackCat') {
            $blackCat = new BlackCat();
            return $blackCat->calculateFee($weight);
        }
        elseif ($companyName == 'Hsinchu') {
            $hsinchu = new Hsinchu();
            return $hsinchu->culateFee($weight/1000);//$weight的单位是 g 而这里需要 kg
        }
        elseif ($companyName == 'PostOffice') {
            $postOffice = new PostOffice();
            return $postOffice->getFee($weight);
        }
        else {
            throw new Exception('No company exception');
        }
    }
}

文章使用了算邮费的例子,这样 ShippingService 就要依赖三种邮费计算 class ,文章中的例子还是比较整齐的,而实际情况可能更糟:

因为三种邮费计算 class 可能不是一个人写的 函数名可能不一样 参数个数和类型也可能不一样。。。就像上面的例子(对原文的例子稍有修改)

2.Interface(依赖反转)

//定义接口
interface LogisticsInterface
{
    /**
     * @param int $weight
     * @return int
     */
    public function calculateFee($weight);
}

//实现接口
class BlackCat implements LogisticsInterface
{
    /**
     * @param int $weight
     * @return int
     */
    public function calculateFee($weight)
    {
        return 100 * $weight * 10;
    }
}

使用了Interface之后就可以写成:

class ShippingService
{
    
    public function calculateFee($companyName, $weight)
    {
        switch ($companyName) {
            case 'BlackCat':
                $logistics = new BlackCat();
            case 'Hsinchu':
                $logistics = new Hsinchu();
            case 'PostOffice':
                $logistics = new PostOffice();
            default:
                throw new Exception('No company exception');
        }
        //有了统一的接口 就可以统一调用
        return $logistics->calculateFee($weight);
    }
}

这样三种邮费类依赖Interface对外提供相同的接口 而ShippingService也依赖于Interface不用担心邮费类发生变化 从而实现了依赖反转

但是 ShippingService 依然要 new 三种邮费出来,依赖于Interface ,邮费类虽然不会变化 但是可能会 去掉或增加 

3.工廠模式

class LogisticsFactory
{

    public static function create(string $companyName)
    {
        switch ($companyName) {
            case 'BlackCat':
                return new BlackCat();
            case 'Hsinchu':
                return new Hsinchu();
            case 'PostOffice':
                return new PostOffice();
            default:
                throw new Exception('No company exception');
        }
    }
}

class ShippingService
{

    public function calculateFee($companyName, $weight)
    {
        $logistics = LogisticsFactory::create($companyName);
        return $logistics->calculateFee($weight);
    }
}

使用工厂模式后 业务层(ShippingService)已经完全和那三个讨厌的家伙说拜拜了 从此完全实现了依赖反转

而即便是这样 ShippingService 也还是同时依赖了工厂类和 interface ,同时也像文章说的还有单元测试的问题, 即程序的5个目标:

  1. 容易維護 
  2. 容易新增功能 
  3. 容易重複使用 
  4. 容易上Git,不易與其他人衝突 
  5. 容易寫測試 

中的第5条还没有实现;况且加一层工厂类显得有点多余(至少在本例中)

在实际开发中情况是这样的:

当两个人合作开发,一个人负责ShippingService 另一个人负责三种邮费计算类(当然工厂类也应该由他来实现);当 ShippingService 开发完成之后 而邮费计算这边还没有完成,这时候要对ShippingService做单元测试,你可以根据 interface 模拟一个邮费计算类,但是你不知道工厂类做了什么,所以就没有办法完全模拟出所依赖模块的行为。

-------------------------------------------------------

其实,由于 PHP 的 interface 只定义了输入 没有限制输出,导致 interface 的行为还是有不确定性

4.Constructor Injection(依赖注入) 

class ShippingService
{
    /** @var LogisticsInterface */
    private $logistics;

    /**
     * ShippingService constructor.
     * @param LogisticsInterface $logistics
     */
    public function __construct(LogisticsInterface $logistics)
    {
        $this->logistics = $logistics;
    }

    /**
     * @param int $weight
     * @return int
     */
    public function calculateFee($weight)
    {
        return $this->logistics->calculateFee($weight);
    }
}

将第三方依赖(邮费计算类)通过参数传入,并指定类型;摒弃了工厂类 实现了最大程度的解耦(只依赖于 interface

5.Method Injection

class ShippingService
{
    /**
     * @param LogisticsInterface $logistics
     * @param int $weight
     * @return int
     */
    public function calculateFee(LogisticsInterface $logistics, $weight)
    {
        return $logistics->calculateFee($weight);
    }
}

这一步主要是解决了原文所说的:“只要 class 要實現依賴注入,唯一的管道就是 constructor injection,若有些相依物件只有單一 method 使用一次,也必須使用 constructor injection,這將導致最後 constructor 的參數爆炸而難以維護的问题并且不必再使用 constructor 与 field,使程序更加精簡

------------------------------------------------------------------

原文中的一段精髓,认为概括的很好:

  • 當A class 去 new B class,A 就是高階模組,B就是低階模組。
  • 高階模組不應該去 new 低階模組,也就是 class,而應該由高階模組定義 interface。

  • 高階模組只依賴自己定義的 interface,而低階模組也只依賴 (實踐) 高階模組所定義的 interface。

 简单说,interface 就是一个 标准 上下游环节都按照该标准完成各自的工作。

© 著作权归作者所有

共有 人打赏支持
dubox
粉丝 3
博文 90
码字总数 25222
作品 0
西安
程序员
依赖注入的简单记录

想理解php依赖注入和控制反转两个概念,就必须搞清楚如下的问题: DI——Dependency Injection 依赖注入 IoC——Inversion of Control 控制反转 1、参与者都有谁?  IOC/DI容器就是一个全局...

金于虎
2016/12/23
4
1
对依赖注入和控制反转的一点理解

1.1、IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制...

舒小贱
2017/12/06
0
0
控制反转( IoC)和依赖注入(DI)

控制反转( IoC)和依赖注入(DI) tags: 容器 依赖注入 IOC DI 控制反转 引言:如果你看过一些框架的源码或者手册,像是laravel或者tp5之类的,应该会提到容器,依赖注入,控制反转等词汇。...

相思叶
03/29
0
0
laravel依赖注入和控制反转

依赖注入与控制反转 依赖注入 当我第一次接触这个词的时候,我是有些丈二和尚摸不着头脑的,至今我也是感到比较困惑的,所以今天我们来探索一下Laravel中的, 来好好的理解它。 控制反转 第一...

eatnothing
2016/05/15
525
0
PHP的依赖注入(DI) 和 控制反转(IoC)

简介 依赖注入和控制反转说的实际上是同一个东西,它们是一种设计模式,这种设计模式用来减少程序间的耦合 优势(为什么使用) 使用依赖注入,最重要的一点好处就是有效的分离了对象和它所需...

ganfanghua
01/12
1
0

没有更多内容

加载失败,请刷新页面

加载更多

00.编译OpenJDK-8u40的整个过程

前言 历经2天的折腾总算把OpenJDK给编译成功了,要说为啥搞这个,还得从面试说起,最近出去面试经常被问到JVM的相关东西,总感觉自己以前学的太浅薄,所以回来就打算深入学习,目标把《深入理...

凌晨一点
26分钟前
0
0
python: 一些关于元组的碎碎念

初始化元组的时候,尤其是元组里面只有一个元素的时候,会出现一些很蛋疼的情况: def checkContentAndType(obj): print(obj) print(type(obj))if __name__=="__main__": tu...

Oh_really
昨天
1
2
jvm crash分析工具

介绍一款非常好用的jvm crash分析工具,当jvm挂掉时,会产生hs_err_pid.log。里面记录了jvm当时的运行状态以及错误信息,但是内容量比较庞大,不好分析。所以我们要借助工具来帮我们。 Cras...

xpbob
昨天
65
0
Qt编写自定义控件属性设计器

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用...

飞扬青云
昨天
1
0
我为什么用GO语言来做区块链?

Go语言现在常常被用来做去中心化系统(decentralised system)。其他类型的公司也都把Go用在产品的核心模块中,并且它在网站开发中也占据了一席之地。 我们在决定做Karachain的时候,考量(b...

HiBlock
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部