六大设计原则 - PHP代码示例分析

一、单一职责原则

解释:顾名思义,就是一个类只负责一个职责

优点:

1、代码的粒度降低了,类的复杂度降低了。
2、可读性提高了,每个类的职责都很明确,可读性自然更好。
3、可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在。
4、改动代码所消耗的资源降低了,更改的风险也降低了。

例如:以下代码场景中需要创建鱼移动方式就会出现问题

/**
 * 动物类
 * Class Animal
 * @package app\api\controller
 */
class Animal{
    /**
     * 移动方式
     * @param $animal
     * @return string
     */
    public function move($animal): string
    {
        return $animal."会爬行";
    }
}

//使用场景
$animal = new Animal();
echo $animal->move("蜘蛛");

/**比如现在有一条鱼 那么就不是爬行因此不能调用$animal->move("鱼");*/

根据上面情况可以使用单一职责原则修改如下

/**单一职责具体修正如下*/

/**
 * 陆地生物类
 * Class Land
 * @package app\api\controller
 */
Class Land{
    /**
     * 移动方式
     * @param $animal
     * @return string
     */
    public function move($animal): string
    {
        return $animal."会爬行";
    }
}

/**
 * 海洋生物类
 * Class Ocean
 * @package app\api\controller
 */
Class Ocean{
    /**
     * 移动方式
     * @param $animal
     * @return string
     */
    public function move($animal): string
    {
        return $animal."会游泳";
    }
}

/**使用场景*/
$animal = new Land();
echo $animal->move("蜘蛛");
$ocean = new Ocean();
echo $ocean->move("鱼");

二、里氏替换原则

解释:在子类中尽量不要重写和重载父类的方法

优点:增强程序的健壮性,版本升级是也可以保持非常好的兼容性.即使增加子类,原有的子类还可以继续运行.在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑

例如下面示例中Operation2类add函数违背里氏替换原则不应该重写父类函数,应该采用抽象方法

//里氏替换原则
Class Operation1{
    public function add($a, $b)
    {
        return $a+$b;
    }
}

Class Operation2 extends Operation1 {

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

根据上面情况可以使用里氏替换原则修改如下

/**里氏替换原则修正如下*/

abstract Class Operation{
    public abstract function add($a,$b);
}


Class Add extends Operation {
    public function add($a, $b)
    {
        return $a+$b;
    }
}

Class Add2 extends Operation{
    public function add($a, $b)
    {
        return $a+$b+2;
    }
}

三、依赖倒置原则

解释:上层模块不应该依赖底层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象

优点:采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性

例如以下示例中当我们需要阅读ipad的时候就不能通过Read类的read函数实现

/**
 * 书籍类
 * Class Book
 * @package app\api\controller
 */
Class Book{
    public function getContent()
    {
        return "book";
    }
}

/**
 * 阅读类
 * Class Read
 * @package app\api\controller
 */
Class Read{
    public function read(Book $book)
    {
        return $book->getContent();
    }
}

//阅读书
(new Read)->read(new Book());

根据上面情况可以使用依赖倒置原则修改如下

/**
 * 读取内容接口
 * Interface ReadInterface
 * @package app\api\controller
 */
interface ReadInterface{
    public function getContent();
}

/**
 * 书籍类
 * Class Book
 * @package app\api\controller
 */
Class Book implements ReadInterface {
    public function getContent()
    {
        return "book";
    }
}

/**
 * Ipad类
 * Class Ipad
 * @package app\api\controller
 */
Class Ipad implements ReadInterface {
    public function getContent()
    {
        return "ipad";
    }
}

/**
 * 阅读类
 * Class Read
 * @package app\api\controller
 */
Class Read{
    public function read(ReadInterface $book)
    {
        return $book->getContent();
    }
}

//这样就可以使用Read类的read函数使用Ipad类
(new Read)->read(new Book());
(new Read)->read(new Ipad());

四、接口隔离原则

解释:客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上

优点:

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  • 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  • 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,*设计冗余的代码。

例如以下示例中

interface Base{
    public function func1();
    public function func2();
}

Class A implements Base{
    public function func1()
    {
        return "A1";
    }

    public function func2()
    {
        //此类中并未使用该方法
    }
}

Class B implements Base{
    public function func1()
    {
        //此类中并未使用该方法
    }

    public function func2()
    {
        return "B2";
    }
}

接口隔离原则修改后如下

interface BaseA{
    public function func1();
}
interface BaseB{
    public function func2();
}

Class A implements BaseA{
    public function func1()
    {
        return "A1";
    }
}

Class B implements BaseB{
    public function func2()
    {
        return "B2";
    }
}

五、迪米特原则

解释:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性

优点:

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

例如以下示例中

Class C{
    function a()
    {
        $a = 10;
        $a += 100;
        return $a;
    }
}

Class D{
    function d(C $c)
    {
        $d = 100;
        $a = $c->a();
        //$a变量从C类中a方法中获取后还对$a进行操作
        $a = $a*10;

        $d +=$a;

        return $d;
    }
}
Class C{
    function a()
    {
        $a = 10;
        $a += 100;
        $a = $a*10;
        return $a;
    }
}

Class D{
    function d(C $c)
    {
        $d = 100;
        $a = $c->a();
        //此处迁移到C类中a方法
        //$a = $a*10;
        $d +=$a;

        return $d;
    }
}

六、开闭原则

解释:类、模块和函数应该对扩展开放,对修改关闭

上一篇:python类中self关键字用法


下一篇:TS 之 泛型工厂