`
ITCheng
  • 浏览: 76810 次
  • 来自: 北京
社区版块
存档分类
最新评论

面向对象的设计原则_里氏代换原则

阅读更多

动机

当我们设计程序模块时,我们会创建一些类层次结构,然后我们通过扩展一些类来创建它们的子类。

我们必须确保子类只是扩展而没有替换父类的功能,否则当我们在已有程序模块中使用它们时将会产生不可预料的结果。

 

里氏代换原则表明当一个程序模块使用基类时,基类的引用可以被子类替换而不影响模块的功能。

 

里氏代换原则

基类完全能够被子类替代而不影响模块的功能。

 

实例

 

对于多态来说里氏代换原则好像是很显然的事情,例如:

Java代码 复制代码 收藏代码
  1. public void drawShape(Shape s) {   
  2. // Code here.   
  3. }  
public void drawShape(Shape s) {
// Code here.
}

 

对于Shape的任何子类来说,drawShape方法都应该能很好的工作。我们必须小心的实现子类以免无意中违反了里氏代换原则,如果一个函数不满足里氏代换原则,那么它可能必须显式地引用子类对象,这样的函数同样违反了开闭原则,因为当添加新的子类时,必须修改它。

考虑下面的矩形类:

Java代码 复制代码 收藏代码
  1. // A very nice Rectangle class.   
  2. public class Rectangle {   
  3.   private double width;   
  4.   private double height;   
  5.   public Rectangle(double w, double h) {   
  6.     width = w;   
  7.     height = h;   
  8.   }   
  9.   public double getWidth() {return width;}   
  10.   public double getHeight() {return height;}   
  11.   public void setWidth(double w) {width = w;}   
  12.   public void setHeight(double h) {height = h;}   
  13.   public double area() {return (width * height);   
  14. }  
// A very nice Rectangle class.
public class Rectangle {
  private double width;
  private double height;
  public Rectangle(double w, double h) {
    width = w;
    height = h;
  }
  public double getWidth() {return width;}
  public double getHeight() {return height;}
  public void setWidth(double w) {width = w;}
  public void setHeight(double h) {height = h;}
  public double area() {return (width * height);
}

 

现在,如果有个正方形呢?显然正方形是一个矩形,所以我们应该让正方形继承矩形类,是这样吗?我们看一下!

 

注意:

  • 正方形不需要同时具有宽和高属性,但是它还是从矩形继承了这些属性。所以,每个正方形都浪费了一点空间,但这不是我们关注的主要问题。
  • 继承而来的setWidth()和setHeight()方法实际上对于正方形是不合适的,因为正方形的宽和高是相等的。所以我们需要重写setWidth()和setHeight()方法,这可能暗示着在这儿并不适合使用继承。

下面是Square类:

Java代码 复制代码 收藏代码
  1. // A Square class.   
  2. public class Square extends Rectangle {   
  3.   public Square(double s) {super(s, s);}   
  4.   public void setWidth(double w) {   
  5.     super.setWidth(w);   
  6.     super.setHeight(w);   
  7.   }   
  8.   public void setHeight(double h) {   
  9.     super.setHeight(h);   
  10.     super.setWidth(h);   
  11.   }   
  12. }  
// A Square class.
public class Square extends Rectangle {
  public Square(double s) {super(s, s);}
  public void setWidth(double w) {
    super.setWidth(w);
    super.setHeight(w);
  }
  public void setHeight(double h) {
    super.setHeight(h);
    super.setWidth(h);
  }
}

 

一切看上去都很好,但是注意下面的代码:

Java代码 复制代码 收藏代码
  1. public class TestRectangle {   
  2.     // Define a method that takes a Rectangle reference.   
  3.     public static void testLSP(Rectangle r) {   
  4.     r.setWidth(4.0);   
  5.     r.setHeight(5.0);   
  6.     System.out.println("Width is 4.0 and Height is 5.0" +   
  7.     ", so Area is " + r.area());   
  8.     if (r.area() == 20.0)   
  9.     System.out.println("Looking good!\n");   
  10.     else  
  11.     System.out.println("Huh?? What kind of rectangle is   
  12.     this??\n");   
  13.     }   
  14.   
  15.     public static void main(String args[]) {   
  16.         // Create a Rectangle and a Square   
  17.         Rectangle r = new Rectangle(1.01.0);   
  18.         Square s = new Square(1.0);   
  19.         // Now call the method above. According to the   
  20.         // LSP, it should work for either Rectangles or   
  21.         // Squares. Does it??   
  22.         testLSP(r);   
  23.         testLSP(s);   
  24.     }   
  25. }  
public class TestRectangle {
	// Define a method that takes a Rectangle reference.
	public static void testLSP(Rectangle r) {
	r.setWidth(4.0);
	r.setHeight(5.0);
	System.out.println("Width is 4.0 and Height is 5.0" +
	", so Area is " + r.area());
	if (r.area() == 20.0)
	System.out.println("Looking good!\n");
	else
	System.out.println("Huh?? What kind of rectangle is
	this??\n");
	}

	public static void main(String args[]) {
		// Create a Rectangle and a Square
		Rectangle r = new Rectangle(1.0, 1.0);
		Square s = new Square(1.0);
		// Now call the method above. According to the
		// LSP, it should work for either Rectangles or
		// Squares. Does it??
		testLSP(r);
		testLSP(s);
	}
}

 

测试程序输出:

Java代码 复制代码 收藏代码
  1. Width is 4.0 and Height is 5.0, so Area is 20.0  
  2. Looking good!   
  3. Width is 4.0 and Height is 5.0, so Area is 25.0  
  4. Huh?? What kind of rectangle is this??  
Width is 4.0 and Height is 5.0, so Area is 20.0
Looking good!
Width is 4.0 and Height is 5.0, so Area is 25.0
Huh?? What kind of rectangle is this??

 

看起来我们违反了里氏代换原则,问题在哪儿?testLSP()方法合理的假设当一个矩形的宽改变时,它的高度不变。当传递一个正方形对象时,该方法却违反了里氏代换原则。从数学上看,正方形是一个矩形,但是一个正方形对象却不是矩形对象,因为一个正方形对象的行为和一个矩形对象的行为不一致。从行为上来说,正方形不是矩形!里氏代换原则清晰的说明,IS-A关系是对于所有的行为来说的,为了遵循里氏代换原则,子类的行为必须和客户端使用的基类的行为一致。

子类不能比基类具有更多的约束,因为必须在任何可以使用基类的地方使用子类,如果子类比基类有更多的约束,那么就会出现基类可用,但却违反了子类约束的情况。

 

总结

里氏代换原则是对开闭原则的扩展,它表明我们在创建基类的新的子类时,不应该改变基类的行为。

分享到:
评论

相关推荐

    里氏代换原则案例程序LSP.zip

    里氏代换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由芭芭拉·里科夫(Barbara Liskov)在1988年提出。该原则规定,子类必须能够替换它们的基类,并且在软件系统中不会产生任何...

    java的里氏代换原则

    里氏代换原则是面向对象设计的基本原则之一,源自于著名数学家贝努利·里氏的一个概念。在Java编程中,它对于理解和构建可扩展、健壮的软件系统至关重要。简单来说,里氏代换原则(LSP,Liskov Substitution ...

    对面向对象设计原则的总结

    对面向对象设计原则的总结,设计模式:“开-闭”原则,里氏代换原则、依赖倒转原则、合成/聚合复用原则、迪米特法则、接口隔离原则

    面向对象设计原则

    面向对象设计原则概述 单一职责原则 开闭原则 里氏代换原则 依赖倒转原则 接口隔离原则 合成复用原则 迪米特法则

    面向对象设计原则PPT

    面向对象设计原则概述 单一职责 开闭原则 里氏代换原则 依赖倒转原则 接口隔离原则 合成复用原则 迪米特法则

    面向对象设计原则和设计模式的概念

    面向对象设计原则与设计模式是软件工程领域的重要组成部分,它们为构建高质量、可维护和可扩展的软件系统提供了指导方针。下面将详细阐述面向对象设计原则及其如何促进软件的复用,以及设计模式的概念。 ### 面向...

    面向对象设计原则源码及文档

    面向对象设计原则是OOPS(Object-Oriented ...这些原则已知的有七个,包括:单一职责原则、开闭原则、里氏代换原则、依赖注入(倒转)原则、接口分离原则、迪米特原则、合成聚合复用原则。(文件包括实例源码及文档)

    面向对象设计原则(Java).ppt

    面向对象设计原则,开闭原则,迪米特法则,里氏代换,接口隔离

    里氏替换原则Demo

    里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,它是由美国计算机科学家芭芭拉·里科夫(Barbara Liskov)提出的。这个原则强调的是在软件工程中,子类型必须能够替换它们的...

    第2章_面向对象设计原则.ppt

    面向对象设计原则概述 单一职责原则 开闭原则 里氏代换原则 依赖倒转原则 接口隔离原则 合成复用原则 迪米特法则

    面向对象设计原则Java概要.ppt

    单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成复用原则和迪米特法则相互补充,共同构成了面向对象设计的基石。理解并熟练应用这些原则,可以有效提高软件开发的效率,降低维护成本,实现...

    Java设计模式02面向对象设计原则

    里氏代换原则主张,任何可以使用基类的地方,都应该能够使用其子类而不影响程序的正确性。这确保了子类可以替换其基类而不改变程序的行为,从而增强了系统的灵活性和稳定性。 **分析与实践** 在实际应用中,LSP...

    面向对象的设计的原则 电子版

    在面向对象设计中,有七个基本原则,即单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成复用原则和迪米特法则。 1. 单一职责原则(SRP):一个类只负责一个功能领域中的相应职责。单一职责...

    面向对象设计原则java

    详细介绍了: 单一职责原则 开闭原则 里氏代换原则 依赖倒转原则 接口隔离原则 合成复用原则 迪米特法则

    第二十八讲:基础三里氏代换原则

    在面向对象设计中,三里氏代换原则(Liskov Substitution Principle,简称LSP)是一个核心的设计原则,它由芭芭拉·里科夫(Barbara Liskov)在1988年提出。这个原则是类型继承关系中的一个重要准则,旨在确保软件...

Global site tag (gtag.js) - Google Analytics