前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式-里氏替换

设计模式-里氏替换

作者头像
yiduwangkai
发布2020-04-24 17:28:27
4020
发布2020-04-24 17:28:27
举报
文章被收录于专栏:大数据进阶大数据进阶

先来看个最正宗的定义 如果对每一个类型为S的对象o1,都有类型为T的对 象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 化,那么类型S是类型T的子类型。 我们再来看一个通俗易懂的定义: 所有引用基类的地方必须能透明地使用其子类的 对象。 下面来看一下uml图

代码语言:javascript
复制
public abstract class AbstractGun {
    //枪用来干什么的?杀敌!
    public abstract void shoot();
}    
//手枪、步枪、机枪的实现类如代码清单2-2所示。 代码清单2-2 手枪、步枪、机枪的实现类
public class Handgun extends AbstractGun { 
    //手枪的特点是携带方便,射程短
    @Override
    public void shoot() {
        System.out.println("手枪射击...");
    } 
}
public class Rifle extends AbstractGun{ 
    //步枪的特点是射程远,威力大
    public void shoot(){
        System.out.println("步枪射击...");
    } 
}
public class MachineGun extends AbstractGun{ 
    public void shoot(){
        System.out.println("机枪扫射...");
    }
}
//有了枪支,还要有能够使用这些枪支的士兵,其源程序如士兵的实现类
public class Soldier { 
    //定义士兵的枪支
    private AbstractGun gun;
    //给士兵一支枪
    public void setGun(AbstractGun _gun){
        this.gun = _gun;
    }

    public void killEnemy(){
        System.out.println("士兵开始杀敌人..."); 
        gun.shoot();
    } 
}
//定义士兵使用枪来杀敌,但是这把枪是抽象的,具体是手枪还是步枪需 要在上战场前(也就是场景中)前通过setGun方法确定
public class Client {
    public static void main(String[] args) {
        //产生三毛这个士兵
        Soldier sanMao = new Soldier(); 
        //给三毛一支枪
        sanMao.setGun(new Rifle()); 
        sanMao.killEnemy();
    } 
}

在项目中难免会遇到一些比较特殊的子类,此时建议采用聚合或者组合来进行

比如如下

代码语言:javascript
复制
public class ToyGun extends AbstractGun { 
    //玩具枪是不能射击的,但是编译器又要求实现这个方法,怎么办?虚构一个呗! 
    @Override
    public void shoot() {
    //玩具枪不能射击,这个方法就不实现了
    } 
}
public class Client {
    public static void main(String[] args) {
        //产生三毛这个士兵
        Soldier sanMao = new Soldier(); 
		sanMao.setGun(new ToyGun()); 
		sanMao.killEnemy();
    } 
}

此种情况建议采用如下的方式进行实现 同样我们来看一下uml图

代码语言:javascript
复制
public class AUG extends Rifle { 
    //狙击枪都携带一个精准的望远镜 
    public void zoomOut(){
        System.out.println("通过望远镜察看敌人..."); 
		public void shoot(){
       		System.out.println("AUG射击...");
    } 
} 
public class Snipper {
    public void killEnemy(AUG aug){
        //首先看看敌人的情况,别杀死敌人,自己也被人干掉 
		aug.zoomOut();
        //开始射击
        aug.shoot();
    } 
}
public class Client {
    public static void main(String[] args) {
        //产生三毛这个狙击手
        Snipper sanMao = new Snipper(); 
        sanMao.setRifle(new AUG()); 
        sanMao.killEnemy();
    } 
}

为了满足设计满足“里氏替换”原则,子类不能太有个性,子类重载父类的方法时,入参要更加宽泛,大于等于父类,返回值要收缩,要小于等于父类。 下面来看一下具体的代码描述

代码语言:javascript
复制
public class Father {
    public Collection doSomething(HashMap map){
        System.out.println("父类被执行..."); 
        return map.values();
    }
}
public class Son extends Father { 
    //放大输入参数类型
    public Collection doSomething(Map map){ 
        System.out.println("子类被执行..."); 
        return map.values();
    } 
}
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();
    } 
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档