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成为可能的主要原则之一,正是子类型的可替换性,才使得使用基类型的模块无需修改的情况下就能进行扩展。这种可替换性是开发人员可以隐式依赖的东西。
分享到:
相关推荐
设计模式是建立在面向对象原则基础上的。面向对象的设计强调类和对象的组织,以及它们之间的相互作用。面向对象设计模式通过类的抽象、类的组织和类之间的通信来实现面向对象设计的优化。 在C#中实现面向对象设计...
在IT行业中,面向对象设计模式是软件开发中的重要概念,特别是在使用C#这样的面向对象编程语言时。设计模式是经过时间验证的、可重用的解决方案,用来解决在软件设计中经常遇到的问题。本视频教程“C#面向对象设计...
在IT行业中,面向对象设计模式是软件开发中的重要概念,特别是在.NET框架,如C#这样的编程语言中。本文将深入探讨“C#面向对象设计模式纵横谈”视频教程及其源码,帮助开发者理解和掌握这些模式在实际项目中的应用。...
在面向对象编程中,设计模式帮助我们遵循一些基本原则,以提高代码的灵活性、可维护性和可扩展性。在这个主题中,我们将深入探讨"单件模式"这一重要的创建型设计模式。 单件模式是一种确保一个类只有一个实例,并...
面向对象设计模式与面向对象原则的关系 面向对象设计模式是建立在良好的面向对象设计基础上的。优秀的面向对象设计能够更好地应对需求的变化,并提高代码的复用性。面向对象设计模式不仅关注于类的设计,还强调了...
面向对象设计原则是软件开发中的核心概念,尤其在Java、C++等面向对象编程语言中至关重要。这些原则为创建可维护、可扩展和可重用的代码提供了指导框架。这篇文章将深入探讨面向对象设计原则,并结合设计模式进行...
### 面向对象自动化测试框架的研究与设计 #### 摘要与引言 本文主要探讨了面向对象自动化测试框架的设计与实现。随着软件规模的不断扩大,传统的手动测试已经无法满足快速迭代的需求,因此自动化测试成为了提高...
本文将深入探讨C#中的面向对象设计模式及其原则,旨在为程序员提供一个理解和应用这些模式的框架。 首先,我们来理解什么是设计模式。设计模式是一种在特定情况下解决问题的通用、可复用的解决方案,它描述了在软件...
而面向对象的C++设计模式则是将这两种概念巧妙融合,为软件开发提供了强大的工具和框架。下面,我们将深入探讨这些关键知识点。 首先,我们要了解**数据结构**。数据结构是指在计算机中组织和存储数据的方式,以...
本文将深入探讨C#中的核心面向对象概念,以及与之相关的UML(统一建模语言)、设计原则和模式。 1. **dotNet概述** .NET是微软推出的一个开发平台,它提供了丰富的库和服务,支持多种语言(如C#、VB.NET、F#等)...
《清华Java语言与面向对象程序设计》是一门深入讲解Java编程和面向对象设计的课程,旨在帮助学习者掌握Java编程的基础知识以及如何运用面向对象的思想进行软件开发。课程内容覆盖了从基本语法到高级特性,从理论概念...
面向对象设计模式是软件开发中的重要概念,尤其是在C#这样的面向对象编程语言中。设计模式是一种在特定场景下解决问题的标准化解决方案,它来源于实践中并被广泛验证为有效的。本篇文章将深入探讨C#中面向对象设计...
6. **面向对象设计原则与设计模式**:第十六章提供了面向对象设计的原则和常用的设计模式,帮助开发者更好地组织代码和解决实际问题。 #### 特色亮点 与其他同类教材相比,《面向对象程序设计与Java语言》具有以下...
通过PPT的学习材料,我们可以深入探讨每个主题,如“设计模式”可以帮助我们更好地理解和应用常见的软件设计解决方案,“UML介绍”让我们掌握统一建模语言,用于系统分析和设计,“面向对象设计原则”如单一职责、开...
### 设计模式基础培训材料-面向对象设计原则与模式 ...以上概述了面向对象设计的基本原则以及23种设计模式,并通过Qt框架中的实例进行了简要说明。理解和应用这些原则和模式对于构建高质量的软件系统至关重要。
6.恰恰是告诉我们用抽象构建框架,用实现扩展细节的注意事项而已:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计...
本系列视频教程旨在通过12天的学习与实践,帮助开发者掌握如何使用PHP语言构建一个面向对象的自定义MVC框架,并应用到实际项目中。 #### 二、MVC架构基础 1. **MVC概述** - **模型(Model)**:负责处理应用程序的...
总结来说,"面向对象分析与设计"主题涵盖了大量的理论知识和实践经验,包括面向对象的基本概念、设计原则、设计模式以及在Java项目中的实际应用。通过学习相关书籍和研究实际项目源码,开发者可以提升自己的面向对象...
9. **设计原则**:作者可能会介绍SOLID原则,包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则,这些原则是指导良好面向对象设计的基石。 10. **软件生命周期**:从需求分析到系统实现,再到...