`
zjliu
  • 浏览: 48454 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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

阅读更多

动机

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

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

 

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

 

里氏代换原则

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

 

实例

 

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

public void drawShape(Shape s) {
// Code here.
}

 

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

考虑下面的矩形类:

// 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类:

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

 

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

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

 

测试程序输出:

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关系是对于所有的行为来说的,为了遵循里氏代换原则,子类的行为必须和客户端使用的基类的行为一致。

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

 

总结

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

分享到:
评论
1 楼 learningBird 2012-10-27  
那请问,如果设计正方形与矩形的关系时,要如何设计好呢?菜鸟请教。

相关推荐

    面向对象原则.zip

    1面向对象6大原则--单一职责原则 2面向对象6大原则--里氏代换原则 3面向对象6大原则--开闭原则 4面向对象6大原则--依赖倒转原则 5面向对象6大原则--合成复用原则 6面向对象6大原则--接口隔离原则

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

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

    面向对象设计原则

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

    面向对象设计原则PPT

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

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

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

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

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

    Java面向对象程序设计杨晓燕面向对象基本原则和模式.pptx

    子类型能够替换基类型原则 子类型能够替换基类型原则也叫里氏代换原则(Liskov Substitution Principle ,简称LSP),里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现,且程序运行正常。 特

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

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

    面向对象设计原则java

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

    面向对象的设计原则 doc

    面向对象设计根本的指导原则是提高可维护性和可复用性。这些原则主要有:1. 开闭原则;2. 依赖倒转原则;3. 里氏代换原则;4. 合成/聚合复用原则;5. 迪米特原则;6. 接口隔离原则

    24种设计模式介绍与6大设计原则

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能...

    设计模式Demo

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能...

    design-pattern-java.pdf

    面向对象设计原则之里氏代换原则 面向对象设计原则之依赖倒转原则 面向对象设计原则之接口隔离原则 面向对象设计原则之合成复用原则 面向对象设计原则之迪米特法则 六个创建型模式 六个创建型模式 简单工厂模式-...

    设计模式详解

    面向对象什么是面向对象?...怎么使用开闭原则银行例子里氏代换原则枪的例子长方形和正方形的例子合成聚合复用原则手机例子依赖倒转原则接口隔离原则管理后台例子全文搜索引擎的设计 迪米特法则购房例子

    《设计模式实训教程》【PPT+类图与代码+样章】

    2.1.4里氏代换原则 2.1.5依赖倒转原则 2.1.6接口隔离原则 2.1.7合成复用原则 2.1.8迪米特法则 2.2实训实例 2.2.1单一职责原则实例分析 2.2.2开闭原则实例分析 2.2.3里氏代换原则实例分析 2.2.4依赖倒转原则...

    酒店客房管理系统源码java-design_model:23种设计模式学习记录

    LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类(父类)可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类(父类),软件单位的功能不受到影响时,基类(父类)才能...

    design_pattern:关于设计模式的理解

    2、里氏代换原则(Liskov Substitution Principle)里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类...

    C++ Design Pattern 23种设计模式(全_解释+源代码).zip

    C++ 23种设计模式(全_解释+源代码).zip 23种设计模式都是基于以下原则,务必记牢 依赖倒转原则 里氏代换原则 开闭原则 接口隔离原则 合成聚合复用原则 迪米特法则 23种设计模式如下:

    C#设计模式(PDF格式)

    C#设计模式(1) 4 一、 C# 面向对象程序设计复习 5 二、 设计模式举例 5 三、 先有鸡还是先有蛋? 7 四、 大瓶子套小瓶子还是小瓶子套大瓶子?...二、 里氏代换原则(LSP) 12 C#设计模式(3) 19 ....

Global site tag (gtag.js) - Google Analytics