面向对象设计原则一:LisKov替换原则(LSP)

来源:互联网 发布:苏州人口数据 编辑:程序博客网 时间:2024/04/26 08:48

●定义

子类型必须能够替换掉它们的基类型。

●关于“IS-A”

通常说“IS-A”是一个继承关系,如果某个将要被创建的类与现存的某个类满足这种关系,那么这个新类应该从这个现存中继承。

“替换原则”告诉我们怎么样去使用面向对象三大机制中的“继承与多态”。C#语言中(很多语言也是这样的)一个子类是可以转换为其基类的,换句话说就是对基类的操作同样适用于其子类。这一点看上去十分简单,但在编写代码的时候,我们却可能会被“IS-A”这个基本的面向对象分析方法所误导,在子类在某些情况下不能够去替换它们的基类。

“敏捷软件开发”一书中例举了一个很好的例子,就是关于矩形与正方形。从通常的“IS-A”关系上来说,正方形就是一个矩形,因而,我们可以从矩形派生一个正方形。但随之问题就来了。这是很明显的,矩形的长与宽是不一样的,所以这个类它需要两个属性用于设置长与宽;而正方形的长与宽是一样,所以如果从矩形继承而来,那么,有一部分东西是正方形不需要的。这不是本质的问题,本质的问题在于正方形与矩形的操作形为是不一样的。假设矩形与正方形都是面积的计算方法,在计算矩形面积时,在设置了长与宽之后(如长4宽5)就可以计算出其面积(面积为20),现在我们的类调用者就是这样做的。但如果换成正方形呢?在设置了长与宽后(假设正方形从矩形继承,并且我们将一个正方形传给了调用者,调用者通过其类来计算其面积)计算其面积时我们就会发现,正方形这个子类是不能够取代其基类矩形的。为什么?由于正方形的长与宽相同,所以当你设置了正方形的长度之后,宽度就会相应改变(变为相等),但这种特性不是矩形所拥有的。显然这不是合适的。

为什么会产生这种问题?这就是“IS-A”所带来的问题。上面所讨论问题的根本之在于“正方形与矩形的行为方式不同”,尽管正方形却是一个长宽相等的矩形。“IS-A”强调的是行为上的“IS-A”,这种形为是外在且公开的。一个类的行为方式才是设计者真正需要关心的东西,也是调用者所需要依赖的东西。所以在继承时需要慎重考虑,这种考虑并不能完全基于“某个东西是一个什么东西”,更重要的是要从其操作行为上分析“某个东西从操作行为上看是一个什么东西”,确保子类不会更改其基类的行为方式。

最后,“IS-A”是OCP原则得以正确应用的主要原则之一,因为它们都是在子类可以替换基类的情况下进行的。违反LisKov替换原则,也会直接或间接的违反OCP原则。

 

附注:面向对象的三个基本特征:

面向对象的三个基本特征是封装、继承、和多态。这三个特征可以使所设计的软件结构稳定,易维护,可复用性强。

  封装将一组相关操作或属性封装到一个类中,对外只提供了必要的属性或方法,而具体的实现对外部隐藏。相比面向过程设计中的模块化的复用性更强。

  继承一方面如同我们这一代会拥有父亲那一代一些特征一样,另一方面又如同我们会有自己独有的个性特征相似。在程序世界里,继承的一个重要作用就是对父类功能的扩展。

  多态则像我们都有父辈的"脸"这个词,但它表现在我们每个人身上却是不同形态的(人脸都不同)。在面向对象编程里,就是父类定义的属性或方法,被不同子类所继承后可以有不同的特征或行为。这种多态的实现方式是重写(重写父类中的方法)。

  从整体来看,封装和继承有相同的功能--达到代码复用的目的。只不过封装后可以在任意地方复用整个类的东西,而继承则是在子类中复用父类中的代码(而子类的东西可以在任意地方复用,则又是封装的作用了)。那么多态的作用是什么呢?它要达到的则是接口的复用,这个接口不是面向对象编程中的接口类型,而是指在父类或接口类型中定义的方法名称等。

  上面主要提到了这三个特征的可复用性的作用,而要实现软件的结构稳定和易维护性则需要三者的协调使用了。也就是与自己的系统架构和每个类的详细设计有关了。

原创粉丝点击