LSP是唯一一个以人名命名的设计原则,并且作者还是一个“女博士” 

=============================================================


LSP,Liskov substitution principle,中文翻译为“里氏替换原则”。

这是面向对象原则中唯一一个以人名命名的原则,尽管Liskov在中国的知名度没有UNIX的几位巨匠(Kenneth Thompson、Dennis Ritchie)、GOF四人帮那么响亮,但查一下资料,你会发现事实上Liskov也是非常牛的:2008年图灵奖获得者,历史上第一个女性计算机博士学位获得者。其具体资料能够在维基百科上查阅:http://en.wikipedia.org/wiki/Barbara_Liskov

言归正传,我们来看看LSP原则究竟是怎么一回事。

LSP最原始的解释当然来源于Liskov女士了,她在1987年的OOPSLA大会上提出了LSP原则,1988年,她将文章发表在ACM的SIGPLAN Notices杂志上,当中详解了LSP原则:

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

英文比較长,看起来比較累,我们简单的翻译并归纳一下:

1) 子类的对象提供了父类的全部行为,且加上子类额外的一些东西(能够是功能,也能够是属性);

2) 当程序基于父类实现时,假设将子类替换父类而程序不须要改动,则说明符合LSP原则

尽管我们略微翻译和整理了一下,但实际上还是非常拗口和难以理解。

幸好还有Martin大师也认为这个不怎么通俗易懂,Robert Martin在1996年为《C++ Reporter》写了一篇题为《The The Liskov Substitution Principle》的文章,解释例如以下:

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

翻译一下就是:函数使用指向父类的指针或者引用时,必须能够在不知道子类类型的情况下使用子类的对象。

Martin大师解释了一下,相对easy理解多了。但Martin大师还不满足,在2002年,Martin在他出版的《Agile   Software   Development   Principles   Patterns   and   Practices》一书中,又进一步简化为:

Subtypes   must   be   substitutable   for   their   base   types。

翻译一下就是:子类必须能替换成它们的父类。

经过Martin大师的两次翻译,我相信LSP原则本身已经解释得比較easy理解了,但问题的关键是:怎样满足LSP原则?或者更通俗的讲:什么情况下子类才干替换父类?

我们知道,对于调用者来说(Liskov解释中提到的P),和父类交互无非就是两部分:调用父类的方法、得到父类方法的输出,中间的处理过程,P是无法知道的。

也就是说,调用者和父类之间的联系体如今双方面:函数输入,函数输出。具体例如以下图:

有了这个图之后,怎样做到LSP原则就清晰了:

1) 子类必须实现或者继承父类全部的公有函数,否则调用者调用了一个父类中有的函数,而子类中没有,执行时就会出错;

2) 子类每一个函数的输入參数必须和父类一样,否则调用父类的代码不能调用子类;

3) 子类每一个函数的输出(返回值、改动全局变量、插入数据库、发送网络数据等)必须不比父类少,否则基于父类的输出做的处理就没法完毕。

有了这三条原则后,就能够非常方便的推断类设计是否符合LSP原则了。须要注意的是第3条的关键是“不比父类少”,也就是说能够比父类多,即:父类的输出是子类输出的子集。

有的朋友看到这三条原则可能有点纳闷:这三条原则一出,那子类还有什么差别哦,岂不都是一样的实现了,那还会有不同的子类么?

事实上假设细致研究这三条原则,就会发现当中仅仅是约定了输入输出,而并没有约束中间的处理过程。比如:相同一个写数据库的输出,A类能够是读取XML数据然后写入数据库,B类能够是从其他数据库读取数据然后本地的数据库,C类能够是通过分析业务日志得到数据然后写入数据库。这3个类的处理过程都不一样,但最后都写入数据到数据库了。

LSP原则最经典的样例就是“长方形和正方形”这个样例。从数学的角度来看,正方形是一种特殊的长方形,但从面向对象的角度来观察,正方形并不能作为长方形的一个子类。原因在于对于长方形来说,设定了宽高后,面积 = 宽 * 高;但对于正方形来说,设定高同一时候就设定了宽,设定宽就同一时候设定了高,最后的面积并非等于我们设定的 宽 * 高,而是等于最后一次设定的宽或者高的平方。

具体代码样比例如以下:

Rectangle.java

package com.oo.java.principles.lsp;

/**
* 长方形
*/
public class Rectangle { protected int _width;
protected int _height; /**
* 设定宽
* @param width
*/
public void setWidth(int width){
this._width = width;
} /**
* 设定高
* @param height
*/
public void setHeight(int height){
this._height = height;
} /**
* 获取面积
* @return
*/
public int getArea(){
return this._width * this._height;
}
}

Square.java

package com.oo.java.principles.lsp;

/**
* 正方形
*/
public class Square extends Rectangle { /**
* 设定“宽”,与长方形不同的是:设定了正方形的宽,同一时候就设定了正方形的高
*/
public void setWidth(int width){
this._width = width;
this._height = width;
} /**
* 设定“高”,与长方形不同的是:设定了正方形的高,同一时候就设定了正方形的宽
*/
public void setHeight(int height){
this._width = height;
this._height = height;
} }

UnitTester.java

package com.oo.java.principles.lsp;

public class UnitTester {

    public static void main(String[] args){
Rectangle rectangle = new Rectangle();
rectangle.setWidth(4);
rectangle.setHeight(5); //例如以下assert推断为true
assert( rectangle.getArea() == 20); rectangle = new Square();
rectangle.setWidth(4);
rectangle.setHeight(5); //例如以下assert推断为false,断言失败,抛出java.lang.AssertionError
assert( rectangle.getArea() == 20);
}
}

上面这个样例同一时候也给出了一个推断子类是否符合LSP的取巧的方法,即:针对父类的单元測试用例,传入子类是否也能够測试通过。假设測试能够通过,则说明符合LSP原则,否则就说明不符合LSP原则

连载:面向对象葵花宝典:思想、技巧与实践(32) - LSP原则的更多相关文章

  1. 连载:面向对象葵花宝典:思想、技巧与实践(28) - 设计原则:内聚&耦合

    前面通过实例解说了一个一环扣一环的面向对象的开发流程:用例模型 -> 领域模型 -> 设计模型(类模型 + 动态模型),解答了面向对象怎样做的问题.接下来我们就要讲"怎样做好面向 ...

  2. IT第二十天 - 面向对象编程思想、抽象类、异常处理、程序操作日志记录、本周总结 ★★★

    IT第二十天 上午 面向对象编程思想 1.组装电脑的设计: (1)电脑的组成:显示器+机箱 (2)机箱的组成:电源+主板+硬盘 (3)主板所包含的部件:cpu+内存+PCI接口+usb接口 (4)PC ...

  3. 黑马程序员——OC语言基础语法 面向对象的思想

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结)(一)基础语法 1)关键字 @interface.@implementati ...

  4. Java面向对象的思想

    面向对象的思想 1.面向对象的思想的特点: ①符合人们的日常思考习惯 ②能将复杂的问题简单化 ③将原来的执行者变为了现在的指挥者 面向对象的思想,所谓的对象,其实就是实体.对于实物的描述,通常有两个方 ...

  5. 面向对象编程思想(OOP)

    本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞 ...

  6. U3D 飞机大战(MVC模式)解析--面向对象编程思想

    在自己研究U3D游戏的时候,看过一些人的简单的游戏开发视频,写的不错,只是个人是java web 开发的人,所以结合着MVC思想,对游戏开发进行了一番考虑. 如果能把游戏更加的思想化,分工化,开发便明 ...

  7. OC - 1.面向过程和面向对象的思想对比

    一.面向过程 1> 思想 面向过程是一种以过程为中心的最基础编程思想,不支持面向对象的特性. 面向过程是一种模块化程序设计方法 2> 开发方法 面向过程的开发方法是以过程(也可以说是模块) ...

  8. .net的自定义JS控件,运用了 面向对象的思想 封装 了 控件(.net自定义控件开发的第一天)

    大家好!我叫刘晶,很高兴你能看到我分享的文章!希望能对你有帮助! 首先我们来看下几个例子 ,就能看到 如何 自定义控件! 业务需求: 制作  一个   属于 自己的    按钮 对象    ,然后 像 ...

  9. 面向对象编程思想(前传)--你必须知道的javascript

    在写面向对象编程思想-设计模式中的js部分的时候发现很多基础知识不了解的话,是很难真正理解和读懂js面向对象的代码.为此,在这里先快速补上.然后继续我们的面向对象编程思想-设计模式. 什么是鸭子类型 ...

随机推荐

  1. 谈数据库索引和Sqlite中索引的使用

    要使用索引对数据库的数据操作进行优化,那必须明确几个问题:1.什么是索引2.索引的原理3.索引的优缺点4.什么时候需要使用索引,如何使用围绕这几个问题,来探究索引在数据库操作中所起到的作用. 1.数据 ...

  2. UVa 1644 (筛素数 + 二分) Prime Gap

    题意: 给出一个整数n,如果n是素数输出0,否则输出它后一个素数与前一个素数的差值. 分析: 首先用筛法把前十万个素数都筛出来,然后放到数组里.用二分找到不大于n的最大的素数的下标,如果这个素数等于n ...

  3. [ahu 1248] NBA Finals

    NBA Finals Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MBTotal Submission: 251 ...

  4. JDK7新特性之fork/join框架

    The fork/join framework is an implementation of the ExecutorService interface that helps you take ad ...

  5. Linux nc命令详解

    NetCat,在网络工具中有“瑞士军刀”美誉,其有Windows和Linux的版本.因为它短小精悍(1.84版本也不过25k,旧版本或缩减版甚至更小).功能实用,被设计为一个简单.可靠的网络工具,可通 ...

  6. ZOJ 3299-Fall the Brick(线段树+离散化)

    题意: n个区间 ,给出区间的左右坐标 ,区间内填满宽度为1的箱子,有m个板子给出板子的高度和左右坐标(同高度不重叠) 所有箱子从上向下落,求每块板子能接到的箱子数. 分析: 首先给的区间很大,一开始 ...

  7. ZOJ 3469 Food Delivery 区间DP

    这道题我不会,看了网上的题解才会的,涨了姿势,现阶段还是感觉区间DP比较难,主要是太弱...QAQ 思路中其实有贪心的意思,n个住户加一个商店,分布在一维直线上,应该是从商店开始,先向两边距离近的送, ...

  8. SVN server 安装 并创建用户访问

    1. 下载svn   官网https://www.visualsvn.com/server/download/ 或者其他网站都行,下载是VisualSVN-Server-3.5.6.msi. 2.安装 ...

  9. HW5.31

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  10. HDU-4616 Game 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4616 比较典型的树形DP题目,f[u][j][k]表示以点u为子树,经过 j 个陷阱的最大值,其中k= ...