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

简单认识里氏替换原则

里氏替换原则(Liskov Substitution Principle LSP):
定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。
我比较倾向定义2:所有引用基类的地方必须能透明地使用子类的对象。
个人理解:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类,但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
适应范围:继承关系

里氏替换原则示例

子类覆盖(重写)父类的方法

我们定义了一个计算类,要求类中主要实现两个数相加。
示例1:

package com.jinit.test;

public class Calculator {
	
	public int count(int a, int b){
		return a+b;
	}

}

调用:

package com.jinit.test;

public class Caller {
	
	public static void main(String[] args) {
		int a = 2;
		int b = 3;
		Calculator calculator = new Calculator();
		int res = calculator.count(a, b);
		System.out.println("a+b="+res);
	}
	
}

运行结果:
运行结果
一切都正常运行。现在来定义一个子类SubClass继承于Calculator类,要求子类实现一个计算两个数相加之后再加10。
示例2

package com.jinit.test;

public class SubClass extends Calculator{
	
	@Override
	public int count(int a, int b){
		return a+b+10;
	}
	
}

调用:

package com.jinit.test;

public class Caller {
	
	public static void main(String[] args) {
		int a = 2;
		int b = 3;
		SubClass subClass = new SubClass();
		System.out.println("父类:a+b="+subClass.count(a, b));
		System.out.println("子类:a+b="+subClass.count(a, b));
	}
	
}

运行结果:
运行结果

可以看出SubClass是继承Calculator类的,按照继承原则,子类SubClass也可以调用父类的计算两个数相加的方法,但是运行结果却出现了错误。
原因就是类SubClass在给方法起名时无意中重写了父类的方法,造成原本运行父类正常的功能出现了错误。

除了重写,重载会出问题吗?

示例3

package com.jinit.test;

import java.util.HashMap;

public class Parent {
	public void doSomething(HashMap map){
		System.out.println("父类方法执行");
	}
}
package com.jinit.test;

import java.util.Map;

public class Son {
	public void doSomething(Map map){
		System.out.println("子类方法执行");
	}
}
package com.jinit.test;

import java.util.HashMap;

public class Caller {
	
	public static void main(String[] args) {
		Son son = new Son();
		HashMap map1 = new HashMap();
		son.doSomething(map1);
	}
	
}

运行结果:
运行结果
子类在没有重写父类方法的前提下,子类方法被执行了,这会引起业务逻辑混乱。

实际开发中运用情况

我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

总结

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。 它包含以下4层含义:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。(示例3)
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。(重写的要求)

采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性,即使增加子类,原有的子类还可以继续运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
里氏代换原则是指程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的。在23种设计模式中,违反里氏代换原则的模式包括以下几种: 1. 模板方法模式:在模板方法中,由子类实现的抽象方法可能会改变模板方法的行为,违反了里氏代换原则。 2. 迭代器模式:在迭代器模式中,子类实现的迭代方法可能会引起迭代器的状态异常,违反了里氏代换原则。 3. 组合模式:在组合模式中,子类实现的添加、删除方法可能会引起整个组合对象的结构异常,违反了里氏代换原则。 4. 工厂方法模式:在工厂方法模式中,子类实现的产品可能会导致工厂方法的返回类型不一致,违反了里氏代换原则。 5. 抽象工厂模式:在抽象工厂模式中,子类实现的产品族可能会导致抽象工厂的返回类型不一致,违反了里氏代换原则。 6. 建造者模式:在建造者模式中,子类实现的部件可能会导致建造者的返回类型不一致,违反了里氏代换原则。 7. 策略模式:在策略模式中,子类实现的具体策略可能会改变策略模式的行为,违反了里氏代换原则。 8. 代理模式:在代理模式中,代理类可能会对被代理类的方法进行过滤或拦截,从而改变系统的行为,违反了里氏代换原则。 这些设计模式虽然违反了里氏代换原则,但是它们都为代码提供了更好的可扩展性和可维护性,具体情况需要根据实际情况进行权衡和选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值