您好,欢迎来到华拓科技网。
搜索
您的当前位置:首页依赖反转 和 依赖注入 (PHP)

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

来源:华拓科技网

读了文章 《 》做下笔记和总结

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

传统实现 —— 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个目标:

中的第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,使程序更加精簡

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

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

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

转载于:https://my.oschina.net/u/2399303/blog/1579635

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo6.cn 版权所有 赣ICP备2024042791号-9

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务