【设计模式】软件设计七大原则 ( 里氏替换原则

您所在的位置:网站首页 mdf4n60b参数代换 【设计模式】软件设计七大原则 ( 里氏替换原则

【设计模式】软件设计七大原则 ( 里氏替换原则

2023-11-27 04:26| 来源: 网络整理| 查看: 265

文章目录 一、里氏替换原则代码示例 ( 类的层级 | 反面示例 )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 */ @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、四边形接口 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; @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; } } 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; } } 三、里氏替换原则代码示例 ( 方法入参 | 正面示例 )

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

如 : 父类的参数是 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 */ @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) 方法"); } } 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 */ @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) 方法"); } } 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 */ @Override public 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() 被执行

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3