里氏代换原则 Liskov Substitution Principle

里氏代换原则 Liskov Substitution Principle 使得开放-封闭成为可能

继承性的思考和说明

  • 继承包含这样一层含义:父类中已经实现的方法,实际上是在设定规范契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修 改,就会对整个继承体系造成破坏

阅读下面一个程序

package liskov;
class A{
	public int func1(int num1, int num2){
		return num1-num2;  }
}
class B extends A{
  public int func1(int a,int b){
    return a+b;}
  public int func2(int a, int b){
    return func1(a,b)+9;}
}

public class Liskov {

	public static void main(String[] args) {
		A a = new A();
		System.out.println("11-3=" +a.func1(11,3));
		System.out.println("1-8="+a.func1(1,8));
		System.out.println ("-------");
		B b = new B();
		System.out.println("11-3=" + b.func1(11,3));
		//这里本意是求出11-3
		System.out.println("1-8="+ b.func1(1,8));
		System.out.println("11+3+9="+b.func2(11,3));
	}
}

程序运行结果为:

11-3=8
1-8=-7
——————
11-3=14
1-8=9
11+3+9=23
发现原来正常的额相减功能发生了错误。
原因:
就是类B重写了父类的方法func1,造成原有功能出现错误。

  • 在实际编程中,常常会通过重写父类的方法完成新的功能, 这样写起来虽然简单,但整个继承体系的复用性会比较差。 特别是运行多态比较频繁的时候。

问题提出:

在编程中,如何正确的使用继承? ——里氏替换 原则
Liskov于1987年提出关于继承的原则——继承必须确 保超类所拥有的性质在子类中仍然成立。”也就是说, 当一个子类的实例能够替换任何其超类的实例时,它们 之间才具有is-A关系。

子类型(subtype)必须能够替换它们的基(父)类型。(子类可以以父类的身份出现) ——这就是里氏代换原则

里氏代换原则的主要作用:

规范继承时子类的一些书写规 则。其主要目的就是保持父类方法不被覆盖

举个例子:

如果鸟是会飞的,企鹅不会飞,企鹅是鸟吗? 类“鸟”中有个方法fly,企鹅自然也继承了这个方法, 可是企鹅不能飞,于是,在企鹅的类中覆盖了fly方法, 告诉方法的调用者:企鹅是不会飞的。这完全符合常理。但是,这违反里氏代换原则(继承必须确保超类 所拥有的性质在子类中仍然成立。),企鹅是鸟的子类, 可是企鹅却不能飞!这显然是不合适的。

所以如何解决呢????

yes——应用里氏替换原则!

通用的解决思路:

让原来的父类和子类都继承一个更通俗的基类, 原有的继承关系去掉, 采用依赖,聚合,组合等关系代替。

所以最开始的问题可以这样解决:

代码如下:

package aa;
//创建-个更加基础的基类
class Base {
//把更加基础的方法和成员写到Base类
}
// A类
class A extends Base {
//返回两个数的差
  public int func1(int num1, int num2) {
	  return num1 - num2;  
	  }
}
class B extends Base{
  A a=new A();
  public int func1(int a,int b){
	  return a+b;	
	  }
  public int func2(int a, int b){
	  return func1(a,b)+9;
	  }
  public int func3(int a, int b){
	  return this.a.func1(a,b);
	  }
}


public class Liskov {
  public static void main(String[] args) {
	A a = new A();
	System.out.println("11-3=" +a.func1(11,3));
	System.out.println("1-8="+a.func1(1,8));
	System.out.println ("-------");
	B b= new B();
	//因为B类不再继承A类,因此调用者,不会再func1是求减法
	//调用完成的功能就会很明确
	System. out.println("11+3=" + b. func1(11, 3));//这里本意是求出11+3
	System. out.println("1+8=" + b.func1(1, 8));// 1+8
	System. out.println("11+3+9=" + b.func2(11, 3));
	//使用组合仍然可以使用到A类相关方法
	System. out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
	}	
}

代码运行结果如下:
里氏代换原则 Liskov Substitution Principle_第1张图片
符合实际要求;
总结:

  • 子类必须实现父类的抽象方法,但不得重写(覆盖)父 类的非抽象(已实现)方法
  • 里氏代换原则规定,子类不能覆写父类已实现的方法。父 类中已实现的方法其实是一种已定好的规范和契约,如 果随意修改它,那么可能会带来意想不到的错误。
  • 子类中可以增加自己特有的方法(可以随时扩展)
    - 子类继承了父类,拥有了父类和方法,同时还可以定义自 己有,而父类没有的方法。这是在继承父类方法的基础 上进行功能的扩展,符合里氏代换原则。

你可能感兴趣的:(设计模式)