- 浏览: 342147 次
- 性别:
- 来自: 广州
-
文章分类
最新评论
一什么是里氏代换原则
里氏代换原则的严格表达是:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1代换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.
换言之,一个软件褓如果使用的量个基类的话,那么一定适用其子类,而且它根本不能察觉出基类对象和子类对象的区别.反过来不成立:如果一个软件实体用的是子类的话,那它不一定适用于基类.
二:Java语言对里氏代换的支持
在编译时期,Java语言编译器会检查一个程序是否符合里氏代换,里氏代换要求凡是基类型使用的地方,子类型一定适用,因此子类必须具备基类的全部接口。举例而言,一个基类Base声明了一个public方法method(),那么其子类sub可否将这个方法的访问权限从public改换成package或者private呢?从里氏代换的角度考察这个问题,就不难得出答案,因为客户端完全有可能调用Base的公开方法,如果以子类类取代之,这个方法却变成了私有的,客户端不能调用,显然这是违反里氏代换法则的,Java编译器根本不会让这样的程序过关。 Java语言对里氏代换支持的局限性:Java编译器不能检查一个系统在实现和商业逻辑上是否满足里氏代换法则,一个著名的例子就是"正方形类是否是长方形类的子类"的问题!!
三:里氏代换原则在设计模式中的体现
A:策略模式:策略模式讲的是:"如果有一组算法,那么将每一个算法封装起来,使得它们可以互换"。封装的概念不难理解,而使得所有的算法可以互换,则需要将所有的具体策略角色放到一个类型等级结构中,使它们拥有共同的接口,显然这种互换性依赖是对里氏代换的遵守:AbstractStrategy s = new ConcreteStratetyA();从这个代码可以看出,客户端依赖于基类类型,而变量的真实类型则是具体的策略,这是个体策略角色可以"即插即用"的关键
B:合成模式
C:代理模式,代理模式给一个对象提供一个代理对象,并由代理对象控制对原对象的引用,代理模式能够成立的关键就在于代理模式与真实模式都是抽象主题角色的子类,客户端只知道抽象主题,而代理主题可以替代抽象主题出现在任何需要的地方,而将真实的主题隐藏在幕后.
四:从代码重构的角度理解
里氏代换讲的是基类和子类的关系,只有当这种关系存在时里氏代换关系才存在.如果两个具体的类A和B之间的关系违反了里氏代换原则的设计,根据具体的情况可以用下面的两种方法进行代码重构:
(1)创建一个新的抽象类C作为两个类的具体的超类,将A和B的共同行为移动到C中,从而解决A和B行为不完全一致的问题.
(2)把A和B的继承关系改写成委派关系.
五:正方形不可以作为长方形的子类
在上面的例子中,如果resize传入的是基类Rectangle对象时,这个resize方法会将宽度不断增加,直到超过长度才停下来.但如果我们在resize方法里传入的是一个Square对象时,这个resize方法就会将正方形的边不断地增加下去,直到溢出为止。这说明什么问题:说明用子类型的对象替换父类型的对象时,得到的结果是不一样的,而里氏代换原则要求子类对象一定是可以替换父类型的对象,这和里氏代换原则相矛盾,故正方形不能是长方形的子类.
我们应当进行如下的代码重构:
如下的测试类是不行的了:
测试代码修改如下:
我们应当尽量从抽象类继承,而不从具体类继承.
我们知道,Properties是HashTable的子类,是一种特殊的HashTable,它只接受String类型的key和value,但是其超类可以接受任何类型的key和value,这就意味着Properties并不能在任何时候取代HashTable,所以Java语言的这个设计也违反了里氏代换原则.
里氏代换原则的严格表达是:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1代换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.
换言之,一个软件褓如果使用的量个基类的话,那么一定适用其子类,而且它根本不能察觉出基类对象和子类对象的区别.反过来不成立:如果一个软件实体用的是子类的话,那它不一定适用于基类.
二:Java语言对里氏代换的支持
在编译时期,Java语言编译器会检查一个程序是否符合里氏代换,里氏代换要求凡是基类型使用的地方,子类型一定适用,因此子类必须具备基类的全部接口。举例而言,一个基类Base声明了一个public方法method(),那么其子类sub可否将这个方法的访问权限从public改换成package或者private呢?从里氏代换的角度考察这个问题,就不难得出答案,因为客户端完全有可能调用Base的公开方法,如果以子类类取代之,这个方法却变成了私有的,客户端不能调用,显然这是违反里氏代换法则的,Java编译器根本不会让这样的程序过关。 Java语言对里氏代换支持的局限性:Java编译器不能检查一个系统在实现和商业逻辑上是否满足里氏代换法则,一个著名的例子就是"正方形类是否是长方形类的子类"的问题!!
三:里氏代换原则在设计模式中的体现
A:策略模式:策略模式讲的是:"如果有一组算法,那么将每一个算法封装起来,使得它们可以互换"。封装的概念不难理解,而使得所有的算法可以互换,则需要将所有的具体策略角色放到一个类型等级结构中,使它们拥有共同的接口,显然这种互换性依赖是对里氏代换的遵守:AbstractStrategy s = new ConcreteStratetyA();从这个代码可以看出,客户端依赖于基类类型,而变量的真实类型则是具体的策略,这是个体策略角色可以"即插即用"的关键
B:合成模式
C:代理模式,代理模式给一个对象提供一个代理对象,并由代理对象控制对原对象的引用,代理模式能够成立的关键就在于代理模式与真实模式都是抽象主题角色的子类,客户端只知道抽象主题,而代理主题可以替代抽象主题出现在任何需要的地方,而将真实的主题隐藏在幕后.
四:从代码重构的角度理解
里氏代换讲的是基类和子类的关系,只有当这种关系存在时里氏代换关系才存在.如果两个具体的类A和B之间的关系违反了里氏代换原则的设计,根据具体的情况可以用下面的两种方法进行代码重构:
(1)创建一个新的抽象类C作为两个类的具体的超类,将A和B的共同行为移动到C中,从而解决A和B行为不完全一致的问题.
(2)把A和B的继承关系改写成委派关系.
五:正方形不可以作为长方形的子类
class Rectangle{ private long width; private long height; public long getWidth() { return width; } public void setWidth(long width) { this.width = width; } public long getHeight() { return height; } public void setHeight(long height) { this.height = height; } }
class Square extends Rectangle{ private long size; public long getWidth() { return getSize(); } public void setWidth(long width) { setSize(width); } public long getHeight() { return getSize(); } public void setHeight(long height) { setSize(height); } public long getSize() { return size; } public void setSize(long size) { this.size = size; } }
public class Test { public void resize(Rectangle r){ int i = 0; while(r.getHeight()<=r.getWidth()){ i++; r.setHeight(r.getHeight() + 1); if(i>100){ break; } System.out.println("******************************************* ************" + i); } } public static void main(String[] args){ Test t = new Test(); Rectangle r = new Rectangle(); r.setHeight(10); r.setWidth(20); Square s = new Square(); s.setSize(10); t.resize(r); //t.resize(s); } }
在上面的例子中,如果resize传入的是基类Rectangle对象时,这个resize方法会将宽度不断增加,直到超过长度才停下来.但如果我们在resize方法里传入的是一个Square对象时,这个resize方法就会将正方形的边不断地增加下去,直到溢出为止。这说明什么问题:说明用子类型的对象替换父类型的对象时,得到的结果是不一样的,而里氏代换原则要求子类对象一定是可以替换父类型的对象,这和里氏代换原则相矛盾,故正方形不能是长方形的子类.
我们应当进行如下的代码重构:
public interface BaseInterface { public long getWidth(); public long getHeight(); } public class MyRectangle implements BaseInterface{ private long width; private long height; public long getWidth() { return width; } public void setWidth(long width) { this.width = width; } public long getHeight() { return height; } public void setHeight(long height) { this.height = height; } }
public class MySquare implements BaseInterface{ private long size; public long getSize() { return size; } public void setSize(long size) { this.size = size; } public long getHeight() { return getSize(); } public long getWidth() { return getSize(); } }那么破坏里氏代换的问题在这里是怎么样避免的呢?秘密在于基类BaseInterface类没有赋值方法,因此resize方法不可能适用于BaseInterface类型,而只能用于不同的具体子类Rectangle和Square,因此里氏代换原则不可能被破坏.
如下的测试类是不行的了:
public class MyTest { public void resize(BaseInterface r){ int i = 0; while(r.getHeight()<=r.getWidth()){ i++; r.setHeight(r.getHeight() + 1);//出错,BaseInterface里面没有这个方法的实现 if(i>100){ break; } System.out.println("******************************************* ************" + i); } } public static void main(String[] args){ MyTest t = new MyTest(); Rectangle r = new Rectangle(); r.setHeight(10); r.setWidth(20); Square s = new Square(); s.setSize(10); t.resize(r);//出错 } }
测试代码修改如下:
public class MyTest { public void resize(Rectangle r){ int i = 0; while(r.getHeight()<=r.getWidth()){ i++; r.setHeight(r.getHeight() + 1);//出错,BaseInterface里面没有这个方法的实现 if(i>100){ break; } System.out.println("******************************************* ************" + i); } } public static void main(String[] args){ MyTest t = new MyTest(); Rectangle r = new Rectangle(); r.setHeight(10); r.setWidth(20); Square s = new Square(); s.setSize(10); t.resize(s); } }
我们应当尽量从抽象类继承,而不从具体类继承.
我们知道,Properties是HashTable的子类,是一种特殊的HashTable,它只接受String类型的key和value,但是其超类可以接受任何类型的key和value,这就意味着Properties并不能在任何时候取代HashTable,所以Java语言的这个设计也违反了里氏代换原则.
发表评论
-
Mina重连
2014-05-26 21:29 2928import com.sun.swing.internal. ... -
面试经典
2014-05-24 09:29 6451.mysql innodb引擎,什么叫聚集索引,与非聚集索 ... -
一拍网网站系统架构图
2014-03-28 21:24 628一拍网网站系统架构图 -
Window下安装配置nginx
2013-08-12 16:53 812安装:http://www.cnblogs.com/wen ... -
使用线程池的好处
2013-07-18 14:41 1259使用线程池有两个好处: 1.可以创建和销毁线程所带来的系统 ... -
Java ThreadLocal使用浅析
2013-07-18 14:36 488ThreadLocal通过在其内部保存变量的副本,并且各个副本 ... -
MyBatis学习之简单增删改查操作、MyBatis存储过程、MyBatis分页、MyBatis一对一、MyBatis一对多
2013-07-05 13:06 1184http://blog.csdn.net/zhangwei ... -
分享一位网友的架构杂谈
2013-05-20 23:16 915不容类型的网站,并发处理不一样,例如针对sns这种类型的网站 ... -
JSP页面静态化
2013-04-08 09:20 896http://www.java-zone.org/644.ht ... -
Java compile to C++
2013-03-19 14:53 509http://code.google.com/a/eclips ... -
几个TCP Socket的通信框架
2013-03-19 12:26 1000http://www.oschina.net/p/simple ... -
宝贝鱼
2013-03-18 23:54 694http://code.google.com/p/cshbbr ... -
将Java程序注册成系统服务(NT服务)
2013-03-16 16:14 604http://blog.csdn.net/small____f ... -
Java内存回收机制
2013-03-13 15:47 815http://www.iteye.com/blogs/tag/ ... -
支付宝,百付宝集成
2013-03-13 14:01 981http://help.alipay.com/support/ ... -
SSH+EXTJS项目下载
2013-03-11 23:02 440http://download.csdn.net/tag/Ex ... -
Hibernate中使用Threadlocal创建线程安全的Session
2013-03-04 20:39 598http://blog.sina.com.cn/s/blog_ ... -
Java Socket多线程通信
2012-10-09 09:53 843当Server没接受到一个Client连接请求之后,都把处理流 ... -
Java 多线程的一个例子
2012-10-09 09:48 1025目录: 1 synchronized的 ... -
app引擎
2012-07-10 09:39 0http://sae.sina.com.cn/ htt ...
相关推荐
里氏代换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由芭芭拉·里科夫(Barbara Liskov)在1988年提出。该原则规定,子类必须能够替换它们的基类,并且在软件系统中不会产生任何...
里氏代换原则是面向对象设计的基本原则之一,源自于著名数学家贝努利·里氏的一个概念。在Java编程中,它对于理解和构建可扩展、健壮的软件系统至关重要。简单来说,里氏代换原则(LSP,Liskov Substitution ...
里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA大会上发表的一篇文章《Data Abstraction and Hierarchy》里面提出来的,主要阐述了有关继承的一些原则,也就是什么时候应该...
**第二十八讲:基础三里氏代换原则** 在面向对象设计中,三里氏代换原则(Liskov Substitution Principle,简称LSP)是一个核心的设计原则,它由芭芭拉·里科夫(Barbara Liskov)在1988年提出。这个原则是类型继承...
C++ 里氏替换原则详解 C++ 里氏替换原则是指在面向对象编程中,子类对象可以替换其基类对象的使用,而不影响程序的逻辑正确性。这个原则是由Barbara Liskov提出的,她认为在子类继承基类时,子类对象应该可以替换...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,它是由美国计算机科学家芭芭拉·里科夫(Barbara Liskov)提出的。这个原则强调的是在软件工程中,子类型必须能够替换它们的...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由Barbara Liskov在1988年提出。该原则指出,子类型必须能够替换它们的基类型而不影响程序的正确性。这意味着在软件系统中,...
7:里氏代换原则;8:依懒倒转原则;9:接口隔离原则;10:合成/聚合复用原则;11:迪米特法原则;12:简单工厂模式;13:工厂方法模式;14:抽象工厂模式;15:单例模式;16:单例模式与MX记录;17:多例模式;18:序列健生成器与单例及...
里氏代换原则是面向对象设计的基本原则之一,它的核心思想是确保软件系统的各个组件能够灵活替换而不影响整体系统的稳定性。这一原则由Barbara Liskov在1988年提出,因此也被称为Liskov替换原则(LSP)。在Java和...
软件设计的七大原则是软件设计的精髓所在,这七大原则分别是开闭原则、里氏代换原则、依赖倒置原则、接口隔离原则、合成/聚合复用原则、迪米特法则和抽象类原则。 一、 开闭原则(OCP) 开闭原则是指一个软件实体...
里氏代换原则规定,子类对象应当能够替换掉父类对象,并且在所有程序中的行为保持一致。这意味着子类不应违背父类的约定,确保子类的兼容性。遵循LSP有助于保证多态性,提高代码的稳定性和可维护性。 4. **依赖...
而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 3、依赖倒转原则(Dependence Inversion Principle) 这个是开闭原则的基础,具体内容:真...
本文将详细探讨六种核心的设计原则,它们分别是开闭原则(Open-Closed Principle, OCP)、里氏代换原则(Liskov Substitution Principle, LSP)、迪米特法则(Law of Demeter, LoD)、依赖倒转原则(Dependency ...
在这个PPT中,主讲人宋航探讨了几个核心的设计原则和模式,包括开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则以及迪米特原则,并介绍了Simple Factory模式。 1. **开闭原则**: 开闭原则是软件设计的核心...
本文将详细解析五个核心的设计原则,它们是面向对象设计的基础,包括开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则以及迪米特原则。 1. **开闭原则**: 开闭原则强调软件实体(如类、模块、函数等)应该对...
综上所述,程序设计的七大原则之一的开-闭原则、里氏代换原则和依赖倒置原则都是面向对象设计中至关重要的概念。它们旨在帮助开发者创建更加健壮、灵活和易于维护的软件系统。通过理解和应用这些原则,可以显著提升...
详细介绍了: 单一职责原则 开闭原则 里氏代换原则 依赖倒转原则 接口隔离原则 合成复用原则 迪米特法则
里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的...
这些原则包括开闭原则、里氏代换原则、依赖倒置原则、接口隔离原则、合成/聚合复用原则、迪米特法则和抽象类原则。这些原则的目的是为了提高软件系统的灵活性、适应性、稳定性和延续性。 一、 开闭原则(OCP) 开...