设计模式六大原则之----里氏替换原则

一、定义

所有引用基类的地方,必须能透明的使用其子类对象。通俗的说:遵循里氏替换原则的代码,只要父类出现的地方就可以使用子类来替换它而不会产生任何错误,使用者不需要知道用的是父类还是子类。
它的核心是继承

友情提醒:xmind导出的图片有点模糊,请放大查看

这里写图片描述

二、优缺点

它的核心是继承,它的优缺点也是继承的优缺点

2.1 优点

  • 代码共享:子类拥有父类的属性和方法
  • 重用性:子类重用父类的代码
  • 子父类异同:子类形似父类,异于父类,子父类不同
  • 扩展性:子类可以随意扩展父类
  • 开放性:父类可以随意被扩展,所以开放性随之增加

2.2 缺点

  • 侵入性:继承是具有侵入性的,强制继承父类的属性和方法
  • 灵活性:降低了了灵活性,子类必须拥有父类的方法,从子类角度看,子类被父类约束了
  • 耦合性:耦合性增强,子类继承了父类的属性和方法,如果修改父类,那么所有子类的逻辑有可能都要修改

三、重点

  • 返回值范围
    • 子类返回值S,父类返回值F,根据里氏替换原则,F范围必须大于S
  • 重载约束
    • 在对象调用时,不能让子类的方法被调用;继承父类时,子类不能重写父类的方法;必须重写时子类的方法参数个数大于父类

四、 注意点

如果想要遵循里氏替换原则,那么尽量避免让子类拥有自己单独的属性和方法,子类个性多了,子父类关系将难以调和

五、案例

路边摆摊打气球,我们可以用玩具枪射击,也可以用仿真枪射击。用代码描述出来。

5.1 遵循里氏替换原则代码实现

/**
 * 枪的抽象类,包含抽象方法和一个成员变量
 *
 * Created by rytong on 2017/11/14.
 */

public abstract class IGun {
    protected static final String TAG = "IGun";

    public abstract void shoot();
}

玩具枪类,仿真枪类似:

/**
 * 玩具枪类
 * Created by rytong on 2017/11/14.
 */

public class PlayerGun extends IGun {

    @Override
    public void shoot() {
        Log.e(TAG,"用玩具枪打气球");
    }
}

打气球的玩家:

/**
 * 打气球的玩家
 * Created by rytong on 2017/11/14.
 */

public class Player {
    IGun gun;

    public Player(IGun gun) {
        this.gun = gun;
    }
    public void shoot(){
        gun.shoot();
    }
}

玩家打气球:

IGun iGun = new PlamingGun();
    new Player(iGun).shoot();

    IGun iGun1 = new PlayerGun();
    new Player(iGun1).shoot();

玩家打了两枪,结果:

Log的TAG是继承的父类,射击方法是实现的父类的抽象方法。

11-14 21:04:49.084 20053-20053/com.designpatterndisclipline E/IGun: 使用仿真枪打气球
11-14 21:04:49.084 20053-20053/com.designpatterndisclipline E/IGun: 用玩具枪打气球

5.2 修改逻辑模拟不严格遵循里氏替换原则可能存在的问题

给IGun增加两个方法,一个内部调用,一个开放出来player调用

/**
 * 枪的抽象类,包含抽象方法和一个成员变量
 *
 * Created by rytong on 2017/11/14.
 */

public abstract class IGun {
    protected static final String TAG = "IGun";

    public abstract void shoot();

    private void preBullet(){
        Log.e(TAG,"先准备好子弹才能射击。");
    }

    public void fire(){
        preBullet();
        shoot();
    }
}

PlayerGun不遵循里氏替换原则,覆盖父类的成员变量和方法,而PlamingGun完全遵循里氏替换原则。

/**
 * 玩具枪类
 * Created by rytong on 2017/11/14.
 */

public class PlayerGun extends IGun {

    private String TAG = "PlayerGun";

    @Override
    public void shoot() {
        Log.e(TAG,"用玩具枪打气球");
    }

    @Override
    public void fire() {
//        super.fire();
        Log.e(TAG,"我在玩玩具枪,不打气球");
    }
}

/**
 * 仿真枪类
 * Created by rytong on 2017/11/14.
 */

public class PlamingGun extends IGun {
    @Override
    public void shoot() {
        Log.e(TAG,"使用仿真枪打气球");
    }
}

修改玩家射击的方法

/**
 * 打气球的玩家
 * Created by rytong on 2017/11/14.
 */

public class Player {
    IGun gun;

    public Player(IGun gun) {
        this.gun = gun;
    }
    public void shoot(){
        gun.fire();
    }
}

玩家射击气球:

IGun iGun = new PlamingGun();
    new Player(iGun).shoot();

    IGun iGun1 = new PlayerGun();
    new Player(iGun1).shoot();

执行结果:

11-14 21:17:50.800 28393-28393/com.designpatterndisclipline E/IGun: 先准备好子弹才能射击。
11-14 21:17:50.800 28393-28393/com.designpatterndisclipline E/IGun: 使用仿真枪打气球
11-14 21:17:50.802 28393-28393/com.designpatterndisclipline E/PlayerGun: 我在玩玩具枪,不打气球

如果设计时规定了要遵循里氏替换原则那么就要严格遵守,否则在编程过程中往往会发生一些难以预料的错误,也就是你的代码的bug率会大大提升。

相关文章

相关标签/搜索