设计模式之二里氏替换原则(LSP)

里氏替换原则

约定了继承的使用规范:

  • 子类应该实现所有的父类方法。
    对于抽象类来说,如果其子类不是抽象类,则必须实现所有的抽象方法。抽象类定义了一个模板,子类实现模板中的方法,在调用的时通过传递抽象类的子类对象决定到底采用抽象类的哪个实现。
    案例:一个场景中,士兵可以使用不同的枪射击敌人。这里可以抽象出三类对象, 场景-士兵-枪,关系如下


    设计模式之二里氏替换原则(LSP)_第1张图片
    场景UML图

    代码如下

AbstractGun.java
public abstract class AbstractGun {
    public abstract void shoot();
}
Pistol.java
public class Pistol extends AbstractGun {
    @Override
    public void shoot() {
        System.out.println("使用手枪射击...biu~~");
    }
}
Rifle.java
public class Rifle extends AbstractGun {
    @Override
    public void shoot() {
        System.out.println("步枪射击... peng~~");
    }
}
MachineGun.java
public class MachineGun extends AbstractGun {
    @Override
    public void shoot() {
        System.out.println("机关枪射击...tu~~");
    }
}
Soldier.java
public class Soldier {
    private AbstractGun gun;
    public void setGun(AbstractGun gun){
        this.gun = gun;
    }
    public void killEnemy(){
        System.out.println("士兵开始射击...");
        gun.shoot();
    }
}
Client.java
public class Client {
    public static void main(String[] args) {
        Soldier soldier = new Soldier();
        soldier.setGun(new Rifle());
        soldier.killEnemy();
    }
}

当我们需要改变使用的枪时候,只需要在Client里面修改setGun的参数,部需要修改Soldier的相关方法,提高了代码可重用性。

  • 子类可以有自己独有的方法和属性。
    基于以上的代码和关系,定义狙击手和狙击枪。


    设计模式之二里氏替换原则(LSP)_第2张图片
    子类可以具有自己的方法
Snipper.java
public class Snipper {
    private AUG aug;
    public void setRifle(AUG aug){
        this.aug = aug;
    }
    public void killEnemy(){
        aug.zoomOut();
        aug.shoot();
    }
}
AUG.java
public class AUG extends Rifle{
    public void zoomOut(){
        System.out.println("开始使用瞄准镜观察~~");
    }
    public void shoot(){
        System.out.println("AUG开始射击... peng~~");
    }
}
  • 覆写或实现父类方法时输入参数可以扩大
Father.java
public class Father{
    public Collection doSomething(HashMap map){
        System.out.println("调用父类----------------------");
        return map.values();
    }
}
Child.java
public class Child extends Father{
    public Collection doSomething(Map map){
        System.out.println("调用子类----------------------");
        return map.values();
    }
}
Client.java
public class Client{
    public static void invoker(){
        Father f = new Father();//-------------------------------------------------------------------
        HashMap map = new HashMap();
        f.doSomething(map);
    }
    public static void main(String[] args) {
        invoker();
    }
}

根据里氏替换规则,父类出现的地方子类就可以出现,所以这里的Father对象可以替换为Child对象。

  • 覆写或实现父类方法时输出结果可以缩小
    针对上一个案例,子类的同名方法的返回值类型必须与父类相同或者是父类的子类。分两种情况,如果是override,正好这是override的要求)如果是overload,类型必须相同,否则不构成overload。

你可能感兴趣的:(设计模式之二里氏替换原则(LSP))