不一样的面向对象(三)

码农天地 -
不一样的面向对象(三)
设计模式六大原则里式替换原则(LSP)

定义:所有引用基类的地方都必须能透明地使用其子类进行替换(简单说就是:子类可以扩展基类的功能,但是不能改变基类原有的功能)

里式替换原则是继承复用的基石,只有当子类可以替换掉基类,且其它功能不受到影响,基类才算真正的能够被复用,子类可以在基类的基础上增加新的方法

里式替换原则核心就是继承,通过继承,引用基类的地方就可以使用子类的对象了

继承的优点

(1)代码共享,每个子类都拥有父类的方法和属性

(2)提高了代码的重用性

(3)子类可以在父类的基础上扩展自己特有的功能

继承的缺点

(1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法

(2)降低代码的灵活性。子类必须拥有父类的属性和方法

(3)增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改

使用里式替换原则注意的点

(1)子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法

(2)子类中可以增加自己特有的方法

(3)当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松

(4)当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格

1、子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法

<?php


abstract class BaseClass
{
    public abstract function abstractAction();
    public function notAbstractAction() {
        echo "我是基类中的非抽象方法".PHP_EOL;
    }
}

class SonClass extends BaseClass {
    public function abstractAction()
    {

        echo "我是子类,我实现了父类的抽象方法";
    }

    public function notAbstractAction()
    {
        echo "我是子类,我重写了基类的非抽象方法";
    }
}

class SonClass2 extends BaseClass {
    public function abstractAction()
    {

        echo "我是子类2,我实现了父类的抽象方法";
    }
    
    public function mySelfAction() {
        echo "我是子类2,这是我特有的方法";
    }
}

//如果将参数由基类替换成子类,该方法输出的结果就会改变
function testFunction(BaseClass $obj) {
    $obj->notAbstractAction();
}

testFunction(new SonClass2());
testFunction(new SonClass());

输出:
我是基类中的非抽象方法
我是子类,我重写了基类的非抽象方

在上边的例子里边,子类SonClass重写了基类中的非抽象发方法notAbstractAction(),如果将参数中的基类替换成SonClass,就会导致打印的结果发生改变。这样就会导致在父类出现的地方,不能由子类完全替换,违背了“里氏替换原则”

2、子类中可以增加自己特有的方法

还是借用上边的那个例子,SonClass2类中实现了自己特有的方法mySelfAction,子类可以拥有自己特有的方法,去实现其他的业务逻辑

依赖倒置原则(DIP)

定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,其核心思想是:要面向接口编程,不要面向实现编程

通俗来说就是:抽象不应该依赖于细节,细节应当依赖于抽象。 换言之,要针对接口编程,而不是针对实现编程

因为细节很容易发生改变,不稳定。而抽象通常是一个规范,不经常改变

依赖倒置原则的实现方法每个类尽量提供接口或抽象类变量的声明类型应该尽量是接口或者是抽象类任何类都不应该从具体类派生使用继承的时候,要尽量遵循上边提到的里式替换原则

代码示例

<?php

//射手类
class Shooter
{
    //每一个射手都能够进行射击
    public function shot(Baili $obj) {
        $obj->shot();
    }
}

class Baili
{
    public function shot() {
        echo "百里开始射击了".PHP_EOL;
    }
}

class Mengya
{
    public function shot() {
        echo "蒙犽开始射击了".PHP_EOL;
    }
}

$shooter = new Shooter();
$shooter->shot(new Baili());

上边的类,正常的功能实现了,但是,因为射手类的射击(shot)这个功能是基于具体的类百里类(Baili)实现的,这就给以后的扩展带来了麻烦。这个时候有一个新的射手蒙犽,但是根据射手类的射击方法,蒙犽没法进行射击,除非对射手类的射击方法进行修改。按理说,只要是射手属性的英雄,就应该能够进行射击

改进,射手类中的射击方法,不应该依赖具体的某一个射手英雄,而应该是射手的泛指

<?php

//射手类
class Shooter implements BaseShooter
{
    //每一个射手都能够进行射击
    public function shot(BaseShooter $obj) {
        $obj->attack();
    }

    public function accack()
    {

    }
}

interface BaseShooter {
    public function shot(BaseShooter $shooter);
    public function accack();
}

class Baili extends Shooter
{
    public function attack() {
        echo "百里开始射击了".PHP_EOL;
    }
}

class Mengya extends Shooter
{
    public function attack() {
        echo "蒙犽开始射击了".PHP_EOL;
    }
}


$shooter = new Shooter();
$shooter->shot(new Baili());

这样再进行扩展的话,就会相对的容易一些,耦合度没有上边那种方式大

特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

php介绍

PHP即“超文本预处理器”,是一种通用开源脚本语言。PHP是在服务器端执行的脚本语言,与C语言类似,是常用的网站编程语言。PHP独特的语法混合了C、Java、Perl以及 PHP 自创的语法。利于学习,使用广泛,主要适用于Web开发领域。

Tags 标签

oop设计模式php

扩展阅读

加个好友,技术交流

1628738909466805.jpg