文档章节

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

dubox
 dubox
发布于 2017/11/25 02:48
字数 1111
阅读 20
收藏 0
点赞 0
评论 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
粉丝 2
博文 75
码字总数 21359
作品 0
西安
程序员
依赖注入的简单记录

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

金于虎 ⋅ 2016/12/23 ⋅ 1

laravel依赖注入和控制反转

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

eatnothing ⋅ 2016/05/15 ⋅ 0

PHP的依赖注入(DI) 和 控制反转(IoC)

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

ganfanghua ⋅ 01/12 ⋅ 0

php 容器和依赖注入容器

最近研究php的框架,知道了依赖注入容器。但是一直弄不清楚怎么回事。依赖注入和控制反转,看代码我的理解是主要用来解耦的,简单说是把关联对象的接口或父抽象类在外部创立,以参数形式注入到...

manbudezhu ⋅ 2016/08/05 ⋅ 0

PHP反射机制实现自动依赖注入

转自 http://blog.csdn.net/qq_20678155/article/details/70158374 依赖注入又叫控制反转,使用过框架的人应该都不陌生。很多人一看名字就觉得是非常高大上的东西,就对它望而却步,今天抽空...

E狼 ⋅ 2017/12/10 ⋅ 0

php中的数种依赖注入

经常看到却一直不甚理解的概念,依赖注入(DI)以及控制器反转(Ioc),找了几篇好的文章,分享一下。 自己理解的,依赖注入就是组件通过构造器,方法或者属性字段来获取相应的依赖对象。 举个现...

杨太化 ⋅ 2015/10/13 ⋅ 0

Spring IOC/DI 设计思想

IOC—Inversion of Control(控制反转): 在java开发中,将你设计好的对象交给了容器控制。传统的程序设计,我们是直接在对象内部通过new进行创建对象,是由程序主动去创建依赖关系对象。I...

C-Kellen ⋅ 2016/06/27 ⋅ 0

Spring的一些基本概念

Spring 关于控制反转(Inversion of Control, IOC)、依赖注入(Denpendcy Injection, DI)、依赖查找(Dependency Lookup) 依赖注入与依赖查找都是控制反转的一个子集。 依赖查找是非常传统的控制...

翟志军 ⋅ 2014/04/08 ⋅ 0

spring-Ioc浅析

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

hello菜bird ⋅ 2016/01/14 ⋅ 0

spring学习——Ioc基础一

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

小风89 ⋅ 2015/10/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 10分钟前 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 12分钟前 ⋅ 0

容器之查看minikue的environment——minikube的环境信息

执行如下命令 mjduan@mjduandeMacBook-Pro:~/Docker % minikube docker-envexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/U......

汉斯-冯-拉特 ⋅ 13分钟前 ⋅ 0

mysql远程连接不上

设置了root所有hosts远程登录,可是远程登录还是失败,原因可能如下: 登录本地数据库 mysql -uroot -p123456 查询用户表 mysql> select user,host,password from mysql.user; 删除密码为空的...

冰公子 ⋅ 13分钟前 ⋅ 0

动态规划小题

凑硬币问题 问题: 有1元、3元、5元面值的硬币若干,要凑到11元需要最少几个硬币? 这是最简单的DP问题,记凑a元需要b个硬币为: n[a] = b。 1)首先,如果凑0元 需要0个硬币表示为 n[0] = 0...

阿豪boy ⋅ 15分钟前 ⋅ 0

shell之切换目录命令cd

让我们来看看linux下简单的 cd 命令,就是切换目录用的,在你当前的目录位置,进入下一个目录 让我们看看这个阿里云 ubuntu 目录,Linux虚拟目录中比较复杂的部分是它如何协调管理各个存储设...

woshixin ⋅ 18分钟前 ⋅ 0

使用 jsoup 模拟登录 urp 教务系统

需要的 jsoup 相关 jar包:https://www.lanzous.com/i1abckj 1、首先打开教务系统的登录页面,F12 开启浏览器调试,注意一下 Request Headers 一栏的 Cookie 选项,我们一会需要拿这个 Cook...

大灰狼时间 ⋅ 53分钟前 ⋅ 0

关于线程的创建

转自自己的笔记: http://note.youdao.com/noteshare?id=87584d4874acdeaf4aa027bdc9cb7324&sub=B49E8956E145476191C3FD1E4AB40DFA 1.创建线程的方法 Java使用Thread类代表线程,所有的线程对......

MarinJ_Shao ⋅ 今天 ⋅ 0

工厂模式学习

1. 参考资料 工厂模式-伯乐在线 三种工厂-思否 深入理解工厂模式 2. 知识点理解 2.1 java三种工厂 简单工厂 工厂模式 抽象工厂 2.2 异同点 逐级复杂 简单工厂通过构造时传入的标识来生产产品...

liuyan_lc ⋅ 今天 ⋅ 0

Java NIO

1.目录 Java IO的历史 Java NIO之Channel Java NIO之Buffer Java NIO之Selector Java NIO之文件处理 Java NIO之Charset Java 可扩展IO 2.简介 “IO的历史”讲述了Java IO API从开始到现在的发...

士别三日 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部