PHP 设计模式学习 设计模式的七大原则(二)

298 阅读4分钟

依赖倒转原则

又叫倒置原则,核心思想就是“面向抽象”编程。这里的抽象可以是“接口”也可以是“抽象类”。

<?php
/**
 * 定义个水果店,卖水果
 * 有很多水果
 */
class FruitShop
{
    public function salesApple(Apple $apple)
    {
        $apple->sales();
    }

    public function salesBanana(Banana $banana)
    {
        $banana->sales();
    }

    public function salesPeach(Peach $peach)
    {
        $peach->sales();
    }
}

class Apple
{
    public function sales()
    {
        echo '卖出一斤苹果';
    }
}

class Banana
{
    public function sales()
    {
        echo '卖出一斤香蕉';
    }
}

class Peach
{
    public function sales()
    {
        echo '卖出一斤桃子';
    }
}

问题

这样设计的程序比较容易理解,从上至下依次进行。但是它有一个巨大的缺点就是上层模块依赖于每一个下层模块,当下层模块变更时,会直接影响到上层

解决方案

为了方便管理这些水果,我们可以抽象出一个“水果”接口(或抽象类)。然后让各类水果都依赖于这个接口,在让我们的水果店也依赖于这个接口。

优化方案

<?php

class FruitShop
{
    public function salesFruits(Fruits $fruits)
    {
        echo $fruits->sales();
    }
}

/**
 * 定义水果抽象类
 */
interface Fruits
{
    public function sales();
}

class Apple implements Fruits
{
    public function sales()
    {
        return  '卖出一斤苹果';
    }
}

class Banana implements Fruits
{
    public function sales()
    {
        return  '卖出一斤香蕉';
    }
}


class Peach implements Fruits
{
    public function sales()
    {
        return  '卖出一斤桃子';
    }
}

总结

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转原则的中心思想应该是面向接口编程
  • 依赖倒转原则是设计理念是:相较于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的架构比细节为基础搭建的架构要稳定的多。在php中,抽象指的是接口或者抽象类,细节就是实现类
  • 使用接口或抽象类的目的是制定号规范,不涉及到具体的操作,把展现的细节的任务交给他们的实现类
  • 依赖关系传递的三种方式 1.接口传递 2.构造方法传递 2.setter方式传递

注意事项

  • 低层模块尽量都要有抽象类或接口,或者两者都有,程序的稳定性更好。
  • 变量声明类型尽量使用抽象类或接口,这样我们的变量引用和实际的对象之间,就存在一个缓冲层,利于程序的扩展和优化
  • 继承时遵循里氏替换原则

里氏替换原则

所有引用基类的地方必须能透明地使用其子类的对象

/**
 * 举例说明继承的风险,我们需要完成一个两数相减的功能,由类A来负责。
 */

class A
{

    public $width;

    public $height;

    public function fun1($a, $b)
    {
        return $a - $b;
    }

}

$a = new A();

echo $a->fun1(100, 50);

class B extends A
{

    public function fun1($a, $b)
    {
        return $a + $b;
    }


    public function fun2($a, $b)
    {
        return $this->fun1($a, $b) + 100;
    }

}


$b = new B();

echo $b->fun2(100, 50);

问题

B类在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了B类重写后的方法,造成原本运行正常的功能fun1出现了错误(错误的原因是减法变成了加法而其他使用者并不知道)。

解决方案

子类可以扩展父类的功能,但不能改变父类原有的功能

代码优化

class A
{

    public $width;

    public $height;

    public function fun1($a, $b)
    {
        return $a - $b;
    }

}

$a = new A();

echo $a->fun1(100, 50);

class B extends A
{

    public function fun3($a, $b)
    {
        return $a + $b;
    }


    public function fun2($a, $b)
    {
        return $this->fun3($a, $b) + 100;
    }

}


$b = new B();

echo $b->fun2(100, 50);


总结

  • 继承包含这样一层含义,父类中凡是已经实现号的方法,实际上是在设定规定和契约,虽然他不强制要求所有的子类必须遵循这些契约,但是如果类对这些已经实现了的方法任意修改,就会对整个继承体系造成破坏
  • 继承在给程序设计带来便利的同时也带来了弊端,比如使用继承会给程序带来侵入性,程序的可移值性降低,增加对象的耦合性。如果一个类被其他的类所继承,则当这个类需要修改的时候,必须要考虑所有的子类,并且父类修改后,可能会对子类的功能照成影响和产生故障
  • 如果对每个类型T1的对象O1,都有类型T2的对象O2,使得以T1定义的所有程序P在在所有的对象O1都替换成O2时,程序P没有发生变化,那么类型T2是类型T1的子类型。换句话来说,所有引用基类的地方能透明的使用其子类的对象
  • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类方法
  • 里氏替换原则告诉我们,继承实际上让两个类的耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖解决问题

其他文章

PHP 设计模式学习 设计模式的七大原则(一)