【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )

举报
韩曙亮 发表于 2022/01/10 23:38:21 2022/01/10
【摘要】 文章目录 一、里氏替换原则代码示例 ( 类的层级 | 反面示例 )1、长方形2、正方形 二、里氏替换原则代码示例 ( 类的层级 | 正面示例 )1、四边形接口2、长方形类3、正方形类 三、...





一、里氏替换原则代码示例 ( 类的层级 | 反面示例 )



在下面的类中 , 定义了长方形类 , 然后定义了长方形类的子类 , 正方形类 ;

其中在 正方形类中 , 重写了 长方形类的方法 ;

该操作明显违反了里氏替换原则 ;

根据里氏替换原则 , 子类只能实现抽象方法 , 不允许覆盖已有的非抽象方法 ;


1、长方形


package liskovsubstitution;

/**
 * 长方形
 */
public class Rectangle {
    /**
     * 长方形长
     */
    private long length;

    /**
     * 长方形宽
     */
    private long width;

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }

    public long getWidth() {
        return width;
    }

    public void setWidth(long width) {
        this.width = width;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2、正方形


package liskovsubstitution;

/**
 * 正方形
 */
public class Square extends Rectangle {
    /**
     * 正方形边长
     */
    private long sideLength;

    public long getSideLength() {
        return sideLength;
    }

    public void setSideLength(long sideLength) {
        this.sideLength = sideLength;
    }

    /**
     * 重写正方形获取长度的方法
     *      正方形的长和宽都是边长
     * @return
     */
    @Override
    public long getLength() {
        return getSideLength();
    }

    /**
     * 重写正方形设置长度的方法
     *      正方形的长和宽都是边长
     * @param length
     */
    @Override
    public void setLength(long length) {
        setSideLength(length);
    }

    /**
     * 重写正方形获取长度的方法
     *      正方形的长和宽都是边长
     * @return
     */
    @Override
    public long getWidth() {
        return getSideLength();
    }

    /**
     * 重写正方形设置宽度的方法
     *      正方形的长和宽都是边长
     * @param width
     */
    @Override
    public void setWidth(long width) {
        setSideLength(width);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59




二、里氏替换原则代码示例 ( 类的层级 | 正面示例 )



子类的行为 要和 父类 保持一致 , 如果无法达到这一点 , 就无法遵守里氏替换原则 ;

使用继承时 , 一定要慎重 ;


1、四边形接口


package liskovsubstitution;

/**
 * 四边形
 *      之前将 正方形 设置为 长方形 的子类不合理
 *      这里抽象出一个四边形接口
 *      令 长方形 和 正方形 都实现该 四边形接口
 */
public interface Quadrangle {
    /**
     * 获取长度
     * @return
     */
    long getLength();

    /**
     * 获取宽度
     * @return
     */
    long getWidth();
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2、长方形类


package liskovsubstitution;

/**
 * 长方形
 */
public class Rectangle implements Quadrangle {
    /**
     * 长方形长
     */
    private long length;

    /**
     * 长方形宽
     */
    private long width;

    @Override
    public long getLength() {
        return this.length;
    }

    @Override
    public long getWidth() {
        return this.width;
    }

    public void setLength(long length) {
        this.length = length;
    }

    public void setWidth(long width) {
        this.width = width;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

3、正方形类


package liskovsubstitution;

/**
 * 正方形
 */
public class Square implements Quadrangle {
    /**
     * 正方形边长
     */
    private long sideLength;


    @Override
    public long getLength() {
        return this.sideLength;
    }

    @Override
    public long getWidth() {
        return this.sideLength;
    }

    public long getSideLength() {
        return sideLength;
    }

    public void setSideLength(long sideLength) {
        this.sideLength = sideLength;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30




三、里氏替换原则代码示例 ( 方法入参 | 正面示例 )



重载 ( 输入参数 宽松 ) : 子类的方法 重载 父类的方法 时 , 方法的前置条件 ( 输入参数 ) , 要比 父类方法的输入参数更宽松 ;

如 : 父类的参数是 HashMap , 如果要符合 里氏替换原则 , 子类如果重载父类方法 , 那么需要使用 Map 类型参数 ;
( 这里注意区分 重写 与 重载 , 重写是重写父类方法 , 重载是函数名相同 , 参数不同 )


如果在父类中参数类型是 Map , 在子类中重载参数类型是 HashMap , 这样就会出现混乱的问题 ;

客户端调用时 , 可能不清楚情况 , 加入传入了 HashMap 参数 , 此时就有可能出现混乱 , 无法调用到 父类/子类的 正常重写方法 , 方法调用被重载方法拦截的情况 ;

如果 重载的方法 的参数 比父类的方法参数更严格 , 那么这就不是重载方法 , 而是重写方法 ;


1、父类


package liskovsubstitution;

import java.util.HashMap;

public class Father {
    public void method(HashMap map) {
        System.out.println("执行父类 void method(HashMap map) 方法");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2、子类


package liskovsubstitution;

import java.util.HashMap;
import java.util.Map;

public class Child extends Father {
    /**
     * 子类重写父类的方法
     * 重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时
     *      方法的 后置条件 ( 返回值 ) 要 比父类更严格或相等 ;
     * @param map
     */
    @Override
    public void method(HashMap map) {
        System.out.println("执行子类重写的 void method(HashMap map) 方法");
    }

    /**
     * 重载的方法
     * 重载 ( 输入参数 宽松 ) : 子类的方法 重载 父类的方法 时
     *      方法的前置条件 ( 输入参数 ) , 要比 父类方法的输入参数更宽松 ;
     *
     * 如果在父类中参数类型是 Map
     *      在子类中重载参数类型是 HashMap
     *      这样就会出现混乱的问题
     * 客户端调用时 , 可能不清楚情况 , 加入传入了 HashMap 参数
     *      此时就有可能出现混乱 , 无法调用到 父类/子类的 正常重写方法
     *      方法调用被重载方法拦截的情况
     *
     * 如果 重载的方法 的参数 比父类的方法参数更严格
     *      那么这就不是重载方法 , 而是重写方法
     *
     * 遵守里氏替换原则很有必要
     * @param map
     */
    public void method(Map map) {
        System.out.println("执行子类重载的 void method(Map map) 方法");
    }

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3、测试类


package liskovsubstitution;

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        HashMap hashMap = new HashMap();

        // 此时传入的 HashMap 参数
        //      由于重载的方法接收的参数是 Map 类型的
        //      此时调用的是父类的方法 或 子类重写的 void method(HashMap map) 方法
        //      不会调用重载的 void method(Map map) 方法
        child.method(hashMap);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

执行结果 :

执行子类重写的 void method(HashMap map) 方法

  
 
  • 1

在这里插入图片描述





四、里氏替换原则代码示例 ( 方法入参 | 反面示例 )



在该反面示例中 , 父类中的方法参数是 Map 类型 , 子类中重载的方法参数是 HashMap 类型 ;

如果客户端调用该方法 , 传入一个 HashMap 类型的参数 , 就会出现只能调用重载方法 , 无法调用父类中定义的方法或子类中重写的方法 ;


重载的方法比父类方法参数严格 , 就会出现上述情况 ;


1、父类


package liskovsubstitution;

import java.util.Map;

public class Father {
    public void method(Map map) {
        System.out.println("执行父类 void method(HashMap map) 方法");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2、子类


package liskovsubstitution;

import java.util.HashMap;
import java.util.Map;

public class Child extends Father {
    /**
     * 子类重写父类的方法
     * 重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时
     *      方法的 后置条件 ( 返回值 ) 要 比父类更严格或相等 ;
     * @param map
     */
    @Override
    public void method(Map map) {
        System.out.println("执行子类重写的 void method(HashMap map) 方法");
    }

    /**
     * 重载的方法
     * 重载 ( 输入参数 宽松 ) : 子类的方法 重载 父类的方法 时
     *      方法的前置条件 ( 输入参数 ) , 要比 父类方法的输入参数更宽松 ;
     *
     * 如果在父类中参数类型是 Map
     *      在子类中重载参数类型是 HashMap
     *      这样就会出现混乱的问题
     * 客户端调用时 , 可能不清楚情况 , 加入传入了 HashMap 参数
     *      此时就有可能出现混乱 , 无法调用到 父类/子类的 正常重写方法
     *      方法调用被重载方法拦截的情况
     *
     * 如果 重载的方法 的参数 比父类的方法参数更严格
     *      那么这就不是重载方法 , 而是重写方法
     *
     * 遵守里氏替换原则很有必要
     * @param map
     */
    public void method(HashMap map) {
        System.out.println("执行子类重载的 void method(Map map) 方法");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

3、测试类


package liskovsubstitution;

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        HashMap hashMap = new HashMap();

        // 此时传入的 HashMap 参数
        //      由于重载的方法接收的参数是 Map 类型的
        //      此时调用的是父类的方法 或 子类重写的 void method(HashMap map) 方法
        //      不会调用重载的 void method(Map map) 方法
        child.method(hashMap);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

执行结果 :

执行子类重载的 void method(Map map) 方法

  
 
  • 1

在这里插入图片描述





五、里氏替换原则代码示例 ( 方法返回值 )



重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时 , 方法的 后置条件 ( 返回值 ) 比父类更严格或相等 ;

如 : 父类的返回值是 Map , 子类的相同的方法 是 Map 或 HashMap ;


该错误基本不可能触犯 , 因为编译时会检查 , 如果发现子类的实现方法的返回值 大于 父类方法 , 编译时会报错 , 下图就是编译报错情况 :

在这里插入图片描述


1、父类


package liskovsubstitution;

import java.util.Map;

public abstract class Father {
    public abstract Map method();
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、子类


package liskovsubstitution;

import java.util.HashMap;

public class Child extends Father {
    /**
     * 子类重写父类的方法
     *      重写方法的返回值 , 严格程度 , 一定要小于等于父类方法的返回值
     * @return
     */
    @Override
    public HashMap method() {
        System.out.println("子类实现的父类的 HashMap method() 被执行");
        HashMap hashMap = new HashMap();
        return hashMap;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3、测试类


package liskovsubstitution;

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.method();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

执行结果 :

子类实现的父类的 HashMap method() 被执行

  
 
  • 1

在这里插入图片描述

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/119897392

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。