软件设计模式sdp-第2章.pptx
- 【下载声明】
1. 本站全部试题类文档,若标题没写含答案,则无答案;标题注明含答案的文档,主观题也可能无答案。请谨慎下单,一旦售出,不予退换。
2. 本站全部PPT文档均不含视频和音频,PPT中出现的音频或视频标识(或文字)仅表示流程,实际无音频或视频文件。请谨慎下单,一旦售出,不予退换。
3. 本页资料《软件设计模式sdp-第2章.pptx》由用户(三亚风情)主动上传,其收益全归该用户。163文库仅提供信息存储空间,仅对该用户上传内容的表现方式做保护处理,对上传内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(点击联系客服),我们立即给予删除!
4. 请根据预览情况,自愿下载本文。本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
5. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007及以上版本和PDF阅读器,压缩文件请下载最新的WinRAR软件解压。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 软件设计 模式 sdp
- 资源描述:
-
1、软件设计模式第2章面向对象程序设计原则提纲 2.1 单一职责原则(SRP)2.2“开/闭”原则(OCP)2.3 接口隔离原则(ISP)2.4 依赖倒置原则(DIP)2.5 Liskov替换原则(LSP)2.1 单一职责原则(SRP)单单一一职责面向对象设计原则强调:一个类或模职责面向对象设计原则强调:一个类或模块应仅有一个引起其变化的因素(职责块应仅有一个引起其变化的因素(职责)。)。通俗地讲,一个类或模块应只承担一个(或一种类型的)业务职责 当类承担的业务职责较多时,该类中任何职责需求的变化都会引起静态实现的变化,和导致其代码不稳定 无论是向目标类添加新的代码,或修改已有的代码,都是对原有代
2、码设计的破坏,导致程序开发者花费巨大成本进行代码修复图2.1 多个业务行为的Patron类例如,COS系统中客户角色具有的业务行为有:登录系统,支付订单,查看菜单等,如图2.1。如果将所有的业务行为实现在客户类Patron中,当这些业务行为中的任何一个需求发生变化时,如支付或登录行为需求发生变化,都需要修改Patron类。即,影响Patron类变化的因素多于1个时,会导致Patron类的代码变得不稳定。图2.1中Patron类的代码框架:图2.2 遵守“单一职责”原则设计的Patron类按照单一职责原则设计Patron类时,可以将登录系统、支付订单等业务职责分别单独地封装在其他类中,从而减少P
3、atron类的业务职责。如图2.2,将登录行为封装在Employee中,支付订单行为封装在PayOrder中。此时,Patron继承Employee和依赖PayOrder,其类的代码框架如下:符合“单一职责”设计原则的图2.2的设计方案减少了影响Patron变化的因素,使其代码更加稳定。工程师在使用“单一职责”原则设计类时,也会产生如下问题:1)设计类数量的增加。如果一个庞大业务系统的所有类按单一职责设计,则有可能导致设计类数量的“爆炸(Explosion)”。此外,设计类数量的增加也会使设计方案复杂度增大。2)类封装特性的破坏。由于数据域封装在目标(实体)类中,如果从目标类中将含有数据域访问
4、逻辑的业务行为分离出去,则势必造成外部代码访问目标类私有域的问题,而最终破坏目标类的封装特性。3)其他问题。完全教条式地运用“单一职责”设计类时,也可能会降低代码的内聚或增加代码的耦合。同时,类职责没有明确的定义,可以是具体业务功能或行为,也可以是抽象逻辑;因此,其边界是模糊的,难以清晰地划定单一职责。2.1“开/闭”原则(OCP)“开开/闭闭”原则要求:类或模块的代码原则要求:类或模块的代码“对扩展是开放的对扩展是开放的”(Open for Extension)、)、“对修改是关闭的对修改是关闭的”(Close for Modification)。)。当软件需求发生变化时,目标类或模块的代码
5、可以通过代码扩展,很容易地实现新的需求;而不是修改已有类或模块的代码。因为,软件代码业务逻辑充满了耦合,当一处代码修改时,将会引发已有代码逻辑变化,产生逻辑错误或制造出新的代码缺陷。图2.3 PayOrder类可扩展性设计 COS系统需求提到的订单支付方式是工资抵扣或现金;在图2.2中,支付订单行为的实现封装在PayOrder类中。如果只考虑这两种支付行为,工程师一般会在PayOrder类中使用分支结构直接实现支付业务逻辑。这种代码结构存在的问题有:1)如果COS支付需求发生变化,则必须修改PayOrder已有的分支结构才能满足新需求;2)由于Patron依赖于PayOrder,则PayOrd
6、er代码的变化会直接影响到依赖者Patron。要解决上面的问题,需要重新设计PayOrder类结构,具体方案见图2.3。在图2.3中,将PayOrder泛化为一个接口或抽象类,其定义抽象的支付行为check();现金支付订单方式的业务逻辑由子类PayByCash实现,工资抵扣支付订单方式的业务逻辑由子类PayByPRDS实现。代码示例如下。PayOrder类结构:PayByCash类结构:PayByPRDS类结构:那么,在图2.3的类设计方案中,当支付行为需求发生变化时,可以定义PayOrder新的子类实现新需求,而不需要修改已有的类(或接口)PayOrder、PayByCash、PayByP
7、RDS等。例如,COS系统升级时,新需求要添加电子银行支付订单方式。那么,工程师可以直接定义PayOrder的子类PayByEBank来实现该需求,如图2.4。图2.4 添加电子银行支付后的PayOrder类结构设计 在图2.4中,PayByEBank子类的添加并不会影响PayByCash、PayByPRDS等已有的类(或接口),且实现了新支付方式的扩展。PayByBank类结构:那么,PayByEBank类的添加对Patron(客户)类是否会产生影响?这种依赖关系不受具体实现类型的影响。因此,PayByEBank类的添加也没有影响到客户类Patron。从可扩展性和代码稳定性角度看,图2.3中
8、PayOrder类的设计方案要优于图2.2,符合“开/闭”原则的设计思想。实施“开/闭”原则设计代码时,工程师可以使用抽象、继承、组合等面向对象技术获得代码灵活性、可重用性、可扩展性等方面的好处。但也应看到,“开/闭”原则对代码还有以下的影响:1)代码可读性降低。由于使用了抽象,代码设计逻辑与业务需求逻辑相比,会产生变化,抽象代码层隐藏了具体业务细节,大大降低了源码的可读性。2)程序测试成本增加。同样地,使用抽象设计会使测试人员无法静态确定具体对象的引用类型,必须等到程序运行时才能确定目标对象的具体类型。因此,代码缺陷可能会滞后到程序运行后才被发现;又或者,程序出现错误后,只有通过动态调试的方
9、法才能有效地定位缺陷。最终,它们都会导致测试成本的增加。2.3 接口隔离原则(ISP)接接口口隔离原则指出:如果某个接口的行为隔离原则指出:如果某个接口的行为不是内聚的,就应该按照业务分组,并将不是内聚的,就应该按照业务分组,并将分组后的业务行为通过隔离的接口单独定分组后的业务行为通过隔离的接口单独定义义。接口的行为要向调用它的客户端提供业务服务;对于不同的业务分组,调用它的客户端是相互独立的;因此,接口提供的服务(分组)也应该是相互独立的。图2.5 打印配送单和发送配送指令行为定义在IDeliver接口中 例如,在COS系统需求中,餐厅员工(Cafeteria Staff)和配餐员(Meal
10、 Deliverer)都有打印配送单(Print Delivery Instructions)的行为;而且,餐厅员工还有发送配送指令(Issue Delivery Request)的行为。如果将打印配送单行为和发送配送指令行为强行定义在接口IDeliver中,如图2.5,将会产生如下问题:1)子类(或实现类)可能会继承(或实现)冗余行为。配餐员MealDeliverer作为IDeliver的实现类,需要实现issueDeliveryQuest()方法;然而,在COS的需求中,配餐员不具有该行为。2)子类(或实现类)的客户端受到不相干的业务行为干扰。假设餐厅员工CafeteriaStaff想要改
11、变issueDeliveryQuest()的行为定义,比如修改方法名称;那么,则要修改接口IDeliver。而IDeliver接口的变化会导致实现类MealDeliverer变化,最终影响到调用MealDeliverer的所有客户端。要解决上面的问题,工程师可以按照接口隔离原则提供的建议将接口中的方法进行业务分组。由于打印打印配送单和发送配送指令是不同的业务行为,两者之间的内聚度很弱,分离它们可以降低相互影响。图2.6 使用“接口隔离”原则设计打印配送单和发送配送指令行为 因此,将图2.5中的IDeliver接口行为printDeliveryInstruction()和issueDeliver
12、yQuest()分别定义在的接口IPrintDelivery和IIssueDelivery中,用于实现行为的隔离;如图2.6所示。IIssueDelivery接口定义了issueDeliveryQuest()行为,IPrintDelivery接口定义了printDeliveryInstruction()行为,彼此独立,互不影响。CafeteriaStaff类实现IIssueDelivery、IPrintDelivery接口,MealDeliverer实现IPrintDelivery接口。可以看到,MealDeliverer只实现了自身需要的业务行为printDeliveryInstructio
13、n(),不用实现冗余行为issueDeliveryQuest(),保证了代码逻辑与需求的一致性。此外,IIssueDelivery接口定义的变化只对其实现类CafeteriaStaff产生影响,而不会对MealDeliverer造成任何影响。图2.6中类图代码框架如下。IIssueDelivery接口:IPrintDelivery接口:在 J a v a 编 程 语 言 中,I P r i n t D e l i v e r y 接 口 可 以 定 义printDeliveryInstruction()行为的默认实现,CafeteriaStaff、MealDeliverer根据需求选择重写或使
14、用接口默认。以CafeteriaStaff为例,代码结构如下。CafeteriaStaff实现类:按照接口隔离原则设计的代码能够避免“接口污染”(指接口的实现类实现了冗余方法,使得代码质量下降),对软件维护或代码重构提供很好地支持。在使用接口隔离原则设计代码时,工程师需要注意以下问题:1)接口数量)接口数量“爆炸爆炸”。按照“接口隔离”原则对目标系统的业务行为分组,将会产生巨大数量的细粒度接口定义;如同类“爆炸”概念一样,当接口过多时,也会导致软件开发或维护成本的急剧上升。2)代码抽象度高。)代码抽象度高。抽象度高的代码具有稳定性、可复用性等优点,面向抽象编程较为推崇这种代码设计思维。但,也应
展开阅读全文