IOS设计模式的六大设计原则之里氏替换原则(LSP,Liskov Substitution Principle)
定义
里氏替换原则的定义有两种,据说是由麻省理工的一位姓里的女士所提出,因此以其名进行命名。
- 定义1:如果对一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1所定义的程序P中在o1全都替换成o2时,程序的行为不发生任何变化,那么T2为T1的子类。
- 定义2:所有引用父类的地方都必须能够透明地使用其子类对象。
定义解读
其实两个定义所表达的意思都相同,就是在所有父类出现的地方,子类都可以出现,并且将父类对象替换为子类对象的时候,程序不会抛出任何异常或者错误,因此我们需要注意的是,尽量不要重载或者重写父类的方法(抽象方法除外),因为这样可能会改变父类原有的行为。
优点
- 代码共享,减少创建类的工作量,每个子类都拥有父类的所有属性和方法;
- 提高代码的可重用性;
- 提高代码的可扩张性;
- 提高产品或项目的开放性。
缺点
- 继承是入侵性的,拥有父类的属性和方法;
- 降低代码的灵活性,必须拥有父类的属性和方法;
- 增强耦合性,父类属性或方法改变,需要考虑子类。
问题提出
有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
解决方案
当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
继承包含这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
示例
还是继续我们上面的场景:这里,我们将Employee类作为父类,它里面有一个计算工资的方法calculateSalary,代码如下所示:
.h文件:
@interface Employee : NSObject // 计算工资
- (void)calculateSalary:(NSString *)name; @end
.m文件:
#import "Employee.h" @implementation Employee - (void)calculateSalary:(NSString *)name
{
NSLog(@"%@的工资是100",name);
} @end
调用代码:
//调用Employee类的calculateSalary方法
Employee *employee = [[Employee alloc] init];
[employee calculateSalary:@"张三"];
[employee release];
输出结果如下:
2013-11-26 16:03:37.058 LiskovSubstitutionPrinciple[3305:303] 张三的工资是100
现在我们为Employee类添加一个子类总监Director类,该类新增了一个职责说明的方法和重写了calculateSalary方法,如下所示:
#import "Director.h" @implementation Director - (void)calculateSalary:(NSString *)name
{
NSLog(@"总监%@的工资是10000",name);
} - (void)duty
{
NSLog(@"总监的职责是管理");
} @end
调用代码:
Employee *employee = [[Director alloc] init]; //将所有父类出现的地方都替换成子类
[employee calculateSalary:@"张三"];
[employee release];
输出结果如下:
2013-11-26 16:15:19.886 LiskovSubstitutionPrinciple[3429:303] 总监张三的工资是10000
从上面的结果我们可以看到,由于重写了父类Employee的calculateSalary方法,造成计算薪资的方法都是调用子类Director重写后的方法。如果应用场景是要求公司的薪资都是统一的,那么调用Director类重写的方法就是不正确的。如果非要重写父类里面的方法,比较通用的做法是:原来的父类和子类都继承一个更通用的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系代替。
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下2层含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
- 子类中可以增加自己特有的方法。
在项目中所有使用子类的地方都可用父类替换,但在调用方法的时候,即呈现面向对象编程的多态性。里氏替换原则,是非常重要的原则,也是相对较难的原则
IOS设计模式的六大设计原则之里氏替换原则(LSP,Liskov Substitution Principle)的更多相关文章
- IOS设计模式的六大设计原则之开放-关闭原则(OCP,Open-Close Principle)
定义 一个软件实体(如类.模块.函数)应当对扩展开放,对修改关闭. 定义解读 在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的.那么如何应对需求变化的情况?这就是开放-关闭原 ...
- Java设计原则之里氏替换原则
里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出.其严格表述如下:如果对每一个类型为S的 ...
- "围观"设计模式(2)--里氏替换原则(LSP,Liskov Substitution Principle)
在面向对象的程序设计中.里氏替换原则(Liskov Substitution principle)是对子类型的特别定义.它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次会议上名为 ...
- 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...
- IOS设计模式的六大设计原则之单一职责原则(SRP,Single Responsibility Principle)
定义 就一个类而言,应该仅有一个引起它变化的原因. 定义解读 这是六大原则中最简单的一种,通俗点说,就是不存在多个原因使得一个类发生变化,也就是一个类只负责一种职责的工作. 优点 类的复杂度降低,一个 ...
- IOS设计模式的六大设计原则之迪米特法则(LOD,Law Of Demeter)
定义 狭义的迪米特法则定义:也叫最少知识原则(LKP,Least Knowledge Principle).如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用.如果其中的一个类需要调用 ...
- IOS设计模式的六大设计原则之接口隔离原则(ISP,Interface Segregation Principle)
定义 客户端不应该依赖它不需要的接口: 一个类对另一个类的依赖应该建立在最小的接口上. 定义解读 定义包含三层含义: 一个类对另一个类的依赖应该建立在最小的接口上: 一个接口代表一个角色,不应该将不同 ...
- IOS设计模式的六大设计原则之依赖倒置原则(DIP,Dependence Inversion Principle)
定义 高层模块不应该依赖于低层模块,二者都应该依赖于抽象:抽象不应该依赖细节:细节应该依赖抽象. 定义解读 依赖倒置原则在程序编码中经常运用,其核心思想就是面向接口编程,高层模块不应该依赖低层模块(原 ...
- 【面向对象设计原则】之里氏替换原则(LSP)
里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个 ...
随机推荐
- 在xcode6中使用矢量图(iPhone6置配UI)
转载出处:http://blog.xoneday.com ios应用程序是一个图像主导的产品.在开发一个应用程序时,你需要各种尺寸的图标,你需要为每个图像文件制作一个@1x尺寸和一个@2x尺寸.这样你 ...
- CLR探索系列:Windbg+SOS动态调试分析托管代码
http://blog.csdn.net/garyye/article/details/4788070 在使用VS进行托管应用程序的调试的时候,有的时候总感觉有些力不从心.譬如查看一个托管堆或者计 ...
- hdu 1863 畅通project
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm& ...
- Linux Bash严重漏洞修复方法
日前Linux官方内置Bash中新发现一个非常严重安全漏洞,黑客可以利用该Bash漏洞完全控制目标系统并发起攻击,为了避免Linux服务器受影响,就要尽快修补该漏洞了.(漏洞参考https://acc ...
- Yii添加验证码
添加带验证码的登陆: 1.先在模型modules下的LoginForm.php定义一个存储验证码的变量:public $verfyCode: 2.然后在rules()方法里定义:array('veri ...
- 【自动部署】服务器自动化操作 RunDeck
RunDeck 是用 Java/Grails 写的开源工具,帮助用户在数据中心或者云环境中自动化各种操作和流程.通过命令行或者web界面,用户可以对任意数量的服务器进行操作,大大降低了对服务器自动化的 ...
- Windows命令行报错:'findstr' 不是内部或外部命令,也不是可运行的程序或批处理文件
环境变量Path中追加:%SystemRoot%/system32;%SystemRoot%;
- 从C转到JAVA学习路之struct与class对比(转)
转自:http://blog.csdn.net/andywxf01/article/details/53506549 JAVA里最牛B的最基本的就是类,而C语言中的struct也可以定义自己的数据结构 ...
- MyBatis批量添加、修改和删除
1.批量添加元素session.insert(String string,Object o) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...
- Memory Barriers
这回该进入主题了. 上一文最后提到了 Memory Barriers ,即内存屏障.由于对一个 CPU 而言,a = 1; b = 1. 由于在中间加了内存屏障,在 X86 架构下,就 ...