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

本文主要是介绍【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 ),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、里氏替换原则代码示例 ( 类的层级 | 反面示例 )
    • 1、长方形
    • 2、正方形
  • 二、里氏替换原则代码示例 ( 类的层级 | 正面示例 )
    • 1、四边形接口
    • 2、长方形类
    • 3、正方形类
  • 三、里氏替换原则代码示例 ( 方法入参 | 正面示例 )
    • 1、父类
    • 2、子类
    • 3、测试类
  • 四、里氏替换原则代码示例 ( 方法入参 | 反面示例 )
    • 1、父类
    • 2、子类
    • 3、测试类
  • 五、里氏替换原则代码示例 ( 方法返回值 )
    • 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;}
}

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*/@Overridepublic long getLength() {return getSideLength();}/*** 重写正方形设置长度的方法*      正方形的长和宽都是边长* @param length*/@Overridepublic void setLength(long length) {setSideLength(length);}/*** 重写正方形获取长度的方法*      正方形的长和宽都是边长* @return*/@Overridepublic long getWidth() {return getSideLength();}/*** 重写正方形设置宽度的方法*      正方形的长和宽都是边长* @param width*/@Overridepublic void setWidth(long width) {setSideLength(width);}
}




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



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

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


1、四边形接口


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

2、长方形类


package liskovsubstitution;/*** 长方形*/
public class Rectangle implements Quadrangle {/*** 长方形长*/private long length;/*** 长方形宽*/private long width;@Overridepublic long getLength() {return this.length;}@Overridepublic long getWidth() {return this.width;}public void setLength(long length) {this.length = length;}public void setWidth(long width) {this.width = width;}
}

3、正方形类


package liskovsubstitution;/*** 正方形*/
public class Square implements Quadrangle {/*** 正方形边长*/private long sideLength;@Overridepublic long getLength() {return this.sideLength;}@Overridepublic long getWidth() {return this.sideLength;}public long getSideLength() {return sideLength;}public void setSideLength(long sideLength) {this.sideLength = sideLength;}
}




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



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

如 : 父类的参数是 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) 方法");}
}

2、子类


package liskovsubstitution;import java.util.HashMap;
import java.util.Map;public class Child extends Father {/*** 子类重写父类的方法* 重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时*      方法的 后置条件 ( 返回值 ) 要 比父类更严格或相等 ;* @param map*/@Overridepublic 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) 方法");}}

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);}
}

执行结果 :

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

在这里插入图片描述





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



在该反面示例中 , 父类中的方法参数是 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) 方法");}
}

2、子类


package liskovsubstitution;import java.util.HashMap;
import java.util.Map;public class Child extends Father {/*** 子类重写父类的方法* 重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时*      方法的 后置条件 ( 返回值 ) 要 比父类更严格或相等 ;* @param map*/@Overridepublic 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) 方法");}
}

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);}
}

执行结果 :

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

在这里插入图片描述





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



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

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


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

在这里插入图片描述


1、父类


package liskovsubstitution;import java.util.Map;public abstract class Father {public abstract Map method();
}

2、子类


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

3、测试类


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

执行结果 :

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

在这里插入图片描述

这篇关于【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/184209

相关文章

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

在Golang中实现定时任务的几种高效方法

《在Golang中实现定时任务的几种高效方法》本文将详细介绍在Golang中实现定时任务的几种高效方法,包括time包中的Ticker和Timer、第三方库cron的使用,以及基于channel和go... 目录背景介绍目的和范围预期读者文档结构概述术语表核心概念与联系故事引入核心概念解释核心概念之间的关系

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令