Liskov替换原则
我们前面说了开闭原则OCP,其背后的主要机制是抽象和多态,但在静态语言中(如c++,java),支持抽象和多态的关键机制之一便是继承,正是有了继承,我们才能够
抽象出接口/基类,然后在子类中实现继承而来的抽象方法,或覆写基类已实现的方法
来定制子类。这样我们才能只通过扩展来实现新增的功能。
但是按照什么规则,我们才能设计出最佳的继承层次呢,以及什么样才是最佳的继承体
系呢?Liskov替换原则回答了这个问题。
下面我们就看看什么是Liskov替换原则(LSP):
LSP:子类型必须能够替换成它们的基类型。
LSP的重要性是不言而喻的,例如:
void f(BaseType bt){
//使用bt来做事情
}
这时候如果我们传递一个子类型SubType的对象st:f(st);
如果这时候f出现一个错误的行为,那么SubType就违反了LSP,同时也导致了对
OCP的违反,因为我们要想这个方法对SubType也产生正确的行为,我们需要重新
修改f,对特定SupType进行定制操作,以便得到正确的行为,这样f对BaseType
的子类就不封闭了
我们看看Bob大叔举的一个违反LSP的例子:
public class Rectangle{
protected int width;
protected int hight;
public void setWidth(int width){
this.width = width;
}
public void setHight(int hight){
this.hight = hight;
}
public int getWidth(){
return width;
}
public int getHight(){
return hight;
}
}
我们经常说继承是"Is-a",而组合是"has-a".这样如果一个的对象对于另一个类的对象
满足"is-a"关系,那么就应该把这个新类从原来那个类继承而来。
正方形是个矩形,因此把Square类视为Rectangle类的子类应该是合理的。"Is"被认为是
面向对象设计(OOA)的基本技术之一。但这会产生微妙但极为应该重视的问题。
但我们首先看到width,height两个变量对于Square来说是一种浪费,一般情况下,这种浪费是无关紧要的。但是setHight和setWidth对于Square来说是不合适的,因为正方形的长和宽应该相同的,我们为了确保这点,我们可以复写这两个方法:
public class Square extends Rectangle{
//others
public void setWidth(int width){
super.setWidth(width);
super.setHight(width);
}
public void setHight(int hight){
super.setHight(hight);
super.setWidth(hight);
}
}
这样似乎就满足了数学意义上的正方形了吧,但是我们考虑下面的函数
void testArea(Rectangle r){
r.setWidth(2);
r.setHeight(3);
assert(r.area()==6);
}
当我们向testArea传递Square对象时,断言就会失败,因为testArea的编写者
不会认为高度的改变,会影响宽度。方法testArea表明Rectangle和Square的结构
是脆弱的,Square不能够替换掉Rectangle,因此Square和Rectagle之间的关系是违反了
LSP。
LSP让我们得出了一个非常重要的结论:一个模型,如果孤立的看,并不能发现问题,模型
的有效性只能通过它的客户程序来表现。如果孤立的看,最后那个版本的模型时自相容的,
但是如果从基类做出一些合理假设的程序员的角度来看,这个模型是有问题的。
但是有谁能知道使用者会做出怎样的假设呢?大多数的假设是很难预测的。事实上如果我们
试图去预测所有这种假设,我们所得到的系统将充满不必要复杂性的Bad Smell.所以通常最好的方法是之预测那些最明显的对于LSP违反情况而推迟其他的预测。
真正原因:
IS-A是关于行为的
Square和Rectangle这个显然合理的模型为什么会出现问题?毕竟Square就是Rectangle,
难道它们之间不存在is-a关系么?
对于不是testArea的编写者来说Square就是Rectangle是没有问题的,但对于testArea得编写者而言,Square对象绝对不是Square,应为Square的行为方式和testArea所期望的行为方式是不相容的。对象的行为方式才是软件真正关注的问题,LSP清楚地告诉我们,OOD中
is-a是对于行为方式而言的,行为方式的合理假设是客户程序所依赖的。
另外一个问题:
我们都知道Java的异常,子类不能比超类抛出更多的异常,这其实就是LSP原则。
结论:
OCP是OOD的核心原则,如果这个原则应用的有效,应用程序就会有更好的维护性、可重用性和健壮性,而LSP是OCP成为可能的主要原则之一,正是子类型的可替换性,才使得使用基类型的模块无需修改的情况下就能进行扩展。这种可替换性是开发人员可以隐式依赖的东西。
分享到:
相关推荐
6.恰恰是告诉我们用抽象构建框架,用实现扩展细节的注意事项而已:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计...
●面向对象设计原则:本书包含了11个面向对象设计原则,涵盖了包的设计和类的设计。这是我所见过的对这方面内容讲解的最清晰、最彻底、最深刻的唯一的一本书。 ●设计模式:本书中讲述了23个设计模式,并都有...
面向对象语言及系统 19 第二节 面向对象的分析 20 OOA分析的任务 20 OOA分析的原则 20 OOA分析过程 20 第三节 面向对象的设计 20 设计的模型 20 设计的三条重要原则 21 面向对象设计的概念 21 面向对象的设计方法 21...
此外,本书还讨论了各种与面向对象概念密切相关的应用主题,包括XML、UML建模语言、持久存储、分布式计算和客户/服务器技术等。 本书内容精炼,示例简单明了,适合各层次面向对象开发人员阅读,也是高校相关专业...
1. 针对特定平台(编程语言、操作系统、数据库、框架、 2. 应用面向对象设计的原则与模式进行优化 1. 单一职责原则SRP 2. 里氏替换原则LSP 3. 依
不必管模式本身所用的语言,可以将模式用于任何面向对象编程语言。 本书是为那些熟悉.NET框架但希望了解如何改进编码方式以及如何运用设计模式、设计原则和最佳实践来提高代码的可维护性和适应性的ASP.NET开发者而写...
2.2 框架设计的基本原则 10 2.2.1 场景驱动设计的原则 11 2.2.2 低门槛原则 17 2.2.3 自说明对象模型原则 20 2.2.4 分层架构原则 25 2.3 小结 27 第3章 命名规范 28 3.1 大小写约定 29 3.1.1 ...
本书为中南大学精品教材立项项目,分为上下两篇共21章,涵盖了面向对象技术中Java开发环境配置、程序设计基础、面向对象原理以及UML的知识。本书使用的开发环境是JDK 1.6+Eclipse 3.3+Rational Rose 2003,逐步引领...
不必管模式本身所用的语言,可以将模式用于任何面向对象编程语言。 《asp.net设计模式》是为那些熟悉.net框架但希望了解如何改进编码方式以及如何运用设计模式、设计原则和最佳实践来提高代码的可维护性和适应性的...
《Cocoa设计模式》介绍了Cocoa框架中用到的面向对象的设计模式。Cocoa框架是Apple的面向对象开发环境,用来开发:Mac OS X的应用程序。主要的开发语言为Objective-C,它是Mac下进行开发必不可少的开发环境。 《Cocoa...
Java基础知识:包括Java语言的特性、面向对象编程、异常处理、集合框架、多线程等。确保你对Java核心概念和相关的API有很好的理解。 数据结构和算法:了解常见的数据结构(如数组、链表、栈、队列、树、图等)以及...
一方面,设计模式教我们如何使用面向对象设计的原则解决各种问题; 另一方面,设计模式定义了一种通用语言(跟框架一样,统一一个标准),让大家在做系统设计的时候,能更有效的交流。 我们学习设计模块的核心...
2.2 框架设计的基本原则 10 2.2.1 场景驱动设计的原则 11 2.2.2 低门槛原则 17 2.2.3 自说明对象模型原则 20 2.2.4 分层架构原则 25 2.3 小结 27 第3章 命名规范 28 3.1 大小写约定 29 3.1.1 ...
java8集合源码关于设计原则、编程语言、工具和框架的说明 设计原则 面向对象编程 在本节中,我们将描述涉及面向对象编程 (OOP) 的关键概念,以及为什么现代系统正在慢慢采用替代技术来替代以前作为理想 OOP 模式教导...
书中不仅讲述面向对象语言(Java、C#和C++)代码,也有专章讨论C这样的过程式语言。作者将理解、测试和修改代码的原理、技术和最新工具(自动化重构工具、单元测试框架、仿对象、集成测试框架等),与解依赖技术和...
GoF 的《设计模式》是所有面向对象语言(C++ Java C#)的基础,只不过不同的语言将之实现得更方便地使用。 GOF 的设计模式是一座"桥" 就 Java 语言体系来说,GOF 的设计模式是 Java 基础知识和 J2EE 框架知识之间一...
ThinkPHP 是一个免费开源的,快速、简单的面向对象的 轻量级PHP开发框架, 遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而 诞生的。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持...