面向对象编程原则.ppt_第1页
面向对象编程原则.ppt_第2页
面向对象编程原则.ppt_第3页
面向对象编程原则.ppt_第4页
面向对象编程原则.ppt_第5页
已阅读5页,还剩39页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

深蓝软件 设计模式(1) 导言:面向对象设计原则 凌宁 微软(中国)有限公司 深蓝软件 目录 1 面向对象的设计原则 2 设计模式概论 3 单件 4 观察者 凌宁 微软(中国)有限公司 深蓝软件 面向对象的设计原则 1单一职责SRP 2.OCP开闭原则 3.里氏代换LSP 4.依赖倒转DIP 5.接口隔离ISP 6.迪米特法则LOD 7合成聚合复用原则(CARP) 凌宁 微软(中国)有限公司 深蓝软件 Booch和Rumbaugh的新的“统一”标识符 凌宁 微软(中国)有限公司 深蓝软件 单一职责SRP 一个优良的系统设计,强调模块间保持低耦合、高内聚的关系,在面向对象 设计中这条规则同样适用,所以面向对象的第一个设计原则就是:单一职责 原则(SRP,Single Responsibility Principle)。 单一职责,强调的是职责的分离,在某种程度上对职责的理解,构成 了不同类之间耦合关系的设计关键,因此单一职责原则或多或少成为设计过 程中一个必须考虑的基础性原则。 1.单一职责原则(SRP) 一个类,最好只做一件事,只有一个引起它变化的原因。 例如,在一个Game类中,可能会具有两个不同的职责,一个职责是维 护创建当前轮的比赛,另一个职责是计算总比赛得分。根据srp原则,着两个 职责应该分离到两个类中,Game类保持维护创建当前轮的比赛,Scorer类负 责计算比赛的得分。 如何要把这两个职责分离到单独的类中呢? 如果一个类承担的职责过多,等于把这些职责耦合在了一起。一个职责的 变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱 的设计,当变化发生时,设计会遭受到意想不到的破坏。 例如,考虑下图的设计。Retangle类具有两方法,如图。一个方法把 矩形绘制在屏幕上,另一个方法计算矩形的面积。 凌宁 微软(中国)有限公司 深蓝软件 有两个不同的Application使用Rectangle类,如上图。一个是计算几何面 积的,Rectangle类会在几何形状计算方面给予它帮助。另一个 Application实质上是绘制一个在舞台上显示的矩形。 Rectangle类具有了两个职责,第一个职责是提供一个矩形形状几何数据 模型;第二个职责是把矩形显示在屏幕上。 对于SRP的违反导致了一些严重的问题。首先,我们必须在计算几何应 用程序中包含核心显示对象的模块。其次,如果绘制矩形Application 发生改变,也可能导致计算矩形面积Application发生改变,导致不必 要的重新编译,和不可预测的失败。 凌宁 微软(中国)有限公司 深蓝软件 一个较好的设计是把这两个职责分离到下图所示的两个完全不同的类中。这 个设计把Rectangle类中进行计算的部分一道GeometryRectangle类中。 现在矩形绘制方式的改变不会对计算矩形面积的应用产生影响了。 凌宁 微软(中国)有限公司 深蓝软件 1.1 什么是职责 在SRP中,我们把职责定义为“变化的原因”(a reason for change)。如果你能够想到多于一个的动 机去改变类,那么这个类就具有多于一个的职责。有时,我们很难注意到这一点。我们习惯于以组 的形式去考虑职责。 class Modem public : void dial(pno:String): ; void hangup(): ; void send(c:Char): ; void recv(): ; 上述Modem接口,大多数人会认为这个接口看起来非常合理。该接口声明了4个函数确实是 Modem所具有的功能。然而,该接口却显示出了两个职责,一个是连接管理(dial+hangup), 第二个是数据通信(send+recv)。 这两个职责应该被分离开么?这依赖于应用程序的变化。 是按照实际情况决定的。如果应用程序的 变化会影响连接管理,那么设计就具有僵化的臭味。因为,调用send和recv的类必须要重新编辑 。在这种情况下,这两个职责应该被分离,这样做会避免这两个职责耦合在一起。 另一方面,如果应用程序的变化总是导致这两方面职责同时变化,那么就不必分离他们。实际上, 分离他们就会具有不必要的复杂性臭味 凌宁 微软(中国)有限公司 深蓝软件 1.2 持久化 上图展示了一种常见的违反SRP的情况,Employee类包含了业务逻辑和对于 持久层的控制。这两个职责在大多数情况下决不应该混合在一起。 业务规则往往会频繁的变化,而持久化的方式却不会如此频繁的变化,并且 变化的原因也是完全不同的。把业务规则和持久模块绑定在一起的做法是不 妥的。当僵化性和脆弱性的臭味变得强烈,那么就应该使用FACADE和 PROXY模式对设计进行重构,分离这两个职责。 小结: SRP是所有原则中最简单的之一,也是最难正确应用的。我们会自然的把职 责结合在一起。软件设计要做的许多内容,就是发现职责并把那些职责相互 分离。分离的原则也不是教条性的,需要应实际需求而定。 凌宁 微软(中国)有限公司 深蓝软件 2.OCP开闭原则 “Closed for Modification; Open for Extension” 是所有面向对象原则的核心。软件设计本身所 追求的目标就是封装变化、降低耦合,而开放封 闭原则正是对这一目标的最直接体现。其他的设 计原则,很多时候是为实现这一目标服务的,例 如以Liskov替换原则实现最佳的、正确的继承层 次,就能保证不会违反开放封闭原则。 OCP的动机很简单:软件是变化的。不论是优质 的设计还是低劣的设计都无法回避这一问题。 OCP说明了软件设计应该尽可能地使架构稳定而 又容易满足不同的需求。 凌宁 微软(中国)有限公司 深蓝软件 为什么要OCP? 通常,对于开发完的代码都需要多种测试才能够投入使用,这包括: 1 设计人员进行初期的架构设计 2 要经过开发人员的单元测试、集成测试。 3 然后再到测试人员的白盒测试、黑盒测试。 4 最后还要由用户进行一定的测试。 经过漫长的测试,代码才能够投入使用。但是软件产品的维护和升级又是一个永恒的 话题,在维护的过程中,你可能要不断地增加一些小功能;在升级的过程中,你要增 加一些较大的功能。 这种功能的扩展,就要求我们改变原有的代码。但是,对原代码的修改就会深刻地影 响到原来的功能的方方面面: 1 可能对旧代码引入了新的错误,使你不得不对旧代码进行大规模的修改。 2 可能引起你不得不重新构造系统的架构。 3 即使新增的代码对旧代码没有影响,你也不得不对原来的系统做一个全面的测试。 4 经过一段时间,也许你认为以前代码更好,更符合用户需求 所有上述列出来的问题,都是对系统功能进行扩展所不能承受的代价。换句话说,我 们设计出来的系统,一定要是扩展性良好的系统。如何才能够设计出扩展性良好的系 统呢?这就需要在软件系统设计时遵守开闭原则 凌宁 微软(中国)有限公司 深蓝软件 玉帝的智慧 玉帝招安美猴王 的例子 不劳师动众、不破坏天规便是“闭”,收仙有道便是“开”。 招安之法便是玉帝天庭的“开一闭”原则,通过给美猴王封 一个“弼马温”的官职,便可使现有系统满足变化了的需求 ,而不必更改天庭的既有秩序 凌宁 微软(中国)有限公司 深蓝软件 如何在OO中引入OCP原则? 把对实体的依赖改为对抽象的依赖就行了。下面的例子说明了这个过程: 05赛季的时候,一辆F1赛车有一台V10引擎。但是到了06赛季,国际汽联修 改了规则,一辆F1赛车只能安装一台V8引擎。车队很快投入了新赛车的研发 ,不幸的是,从工程师那里得到消息,旧车身的设计不能够装进新研发的引 擎。我们不得不为新的引擎重新打造车身,于是一辆新的赛车诞生了。但是 ,麻烦的事接踵而来,国际汽联频频修改规则,搞得设计师在“赛车”上改了又 改,最终变得不成样子,只能把它废弃。 凌宁 微软(中国)有限公司 深蓝软件 为了能够重用这辆昂贵的赛车,工程师们提出了解决方案 :首先,在车身的设计上预留出安装引擎的位置和管线。 然后,根据这些设计好的规范设计引擎(或是引擎的适配 器)。于是,新的赛车设计方案就这样诞生了。 凌宁 微软(中国)有限公司 深蓝软件 做到开闭原则,就注意以下两点。 1)多使用抽象类 在设计类时,对于拥有共同功能的相 似类进行抽象化处理,将公用的功能 部分放到抽象类中,所有的操作都调 用子类。这样,在需要对系统进行功 能扩展时,只需要依据抽象类实现新 的子类即可。如图10-1所示,在扩展 子类时,不仅可以拥有抽象类的共有 属性和共有函数,还可以拥有自定义 的属性和函数。 凌宁 微软(中国)有限公司 深蓝软件 2)多使用接口 与抽象类不同,接口只定义子类应该实现的接口函数,而不实现公有 的功能。在现在大多数的软件开发中,都会为实现类定义接口,这样在 扩展子类时实现该接口。如果要改换原有的实现,只需要改换一个实现 类即可。 如图各子类由接口类定义了接口函数,只需要在不同的子类中编写不同 的实现即可,当然也可以实现自有的函数。 凌宁 微软(中国)有限公司 深蓝软件 Liskov(女程序员)替换原则 在一个软件系统中,子类应该可以替换任何基类 能够出现的地方,并且经过替换以后,代码还能 正常工作。 第一个例子:正方形不是长方形 “正方形不是长方形”是一个理解里氏代换原则 的最经典的例子。在数学领域里,正方形毫无疑 问是长方形,它是一个长宽相等的长方形。所以 ,我们开发的一个与几何图形相关的软件系统中 ,让正方形继承自长方形是顺利成章的事情。现 在,我们截取该系统的一个代码片段进行分析: 凌宁 微软(中国)有限公司 深蓝软件 正方形不是长方形 长方形类Rectangle: class Rectangle double length; double width; public : double getLength() return length; void setLength(double height) this.length = length; double getWidth() return width; void setWidth(double width) this.width = width; 正方形类Square: class Square :public Rectangle public : void setWidth(double width) Rectangle :setLength(width); Rectangle : setWidth(width); void setLength(double length) Rectangle :.setLength(length); Rectangle :.setWidth(length); 凌宁 微软(中国)有限公司 深蓝软件 正方形不是长方形 由于正方形的度和宽度必须相等,所以在方法setLength和setWidth中 ,对长度和宽度赋值相同。类TestRectangle是我们的软件系统中的 一个组件,它有一个resize方法要用到基类Rectangle,resize方法的 功能是模拟长方形宽度逐步增长的效果: 测试类TestRectangle: class TestRectangle public: void resize(Rectangle &objRect) while(objRect.getWidth() 抽象层-底层类。 (High Level Classes(高层模块) Abstraction Layer(抽 象接口层) Low Level Classes(低层模块)。 缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。 凌宁 微软(中国)有限公司 深蓝软件 解决办法一 将Light作成Abstract,然后具体类继承自 Light。 优点:ToggleSwitch依赖于抽象类Light, 具有更高的稳定性,而BulbLight与 TubeLight继承自Light,可以根据“开放 封闭”原则进行扩展。 只要Light不发生变化,BulbLight与 TubeLight的变化就不会波及ToggleSwitch 。 缺点:如果用ToggleSwitch控制一台电视 就很困难了。总不能让TV继承自Light吧。 凌宁 微软(中国)有限公司 深蓝软件 解决办法一 凌宁 微软(中国)有限公司 深蓝软件 接口隔离ISP 一、ISP简介(ISP-Interface Segregation Principle): 第一:客户端不应该依赖他不需要的接口也就是对接口的细化 纯洁; 第二:类直接的依赖应该建立在最小的接口上面; 第三:建立单一的接口 几个模块就要有及格接口 而不是一个庞大的 臃肿的接口; 其他:接口是对外的承诺,承诺的越少,月利于开发;但是开发的过 程中也要注意一个度的概念,否则接口太多也不利于维护; 在我们进行设计的时候,一个重要的工作就是恰当地划分角色和角色 对应的接口。因此,这里的接口往往有两种不同的含义。 凌宁 微软(中国)有限公司 深蓝软件 二、举例说明: 1接口对应的角色 指一个类型所具有的方法特征的集 合,仅仅是一种逻辑上的抽象,接 口的划分就直接带来类型的划分。 这里,我们可以把接口理解成角色 ,一个接口只是代表一个角色,每 个角色都有它特定的一个接口,这 里的这个原则可以叫做角色隔离原 则。 例如,我们将电脑的所有功能角色 集合为一起,构建了一个接口,如 图10-3所示。 凌宁 微软(中国)有限公司 深蓝软件 此时,我的电脑和你的电 脑要实现该接口,就必须 实现所有的接口函数,显 然接口混乱,并不能够满 足实际的需求: 我的电脑可能是用来工作 和学习的,你的电脑可能 是用来看电影、上网和打 游戏等娱乐活动的,那我 们就可以将电脑的角色划 分为两类,如图10-4所示 。 凌宁 微软(中国)有限公司 深蓝软件 2角色对应的接口 指某种语言具体的接口定义,有严格的定义和结构。比如Java语言里 面的Interface结构。对不同的客户端,同一个角色提供宽窄不同的接 口,也就是定制服务,仅仅提供客户端需要的行为,客户端不需要的 行为则隐藏起来。 对于图10-4中的接口定义,如果我的电脑除了工作和学习之外,还想 上网,那就没办法了,必须实现娱乐电脑的接口,这样就必须实现它 的所有接口函数了。此时我们需要将对应角色中的接口再进行划分, 如图10-5所示。 凌宁 微软(中国)有限公司 深蓝软件 这样,经过以上的划分,如果我的电脑想 增加某一项功能,只需要继承不同的接口 类即可。 由此可见,对接口角色的划分,是从大的 类上进行划分的;对角色的接口进行的划 分,是对类的接口函数的划分。它们两者 由粗到细,实现了接口的完全分离。 凌宁 微软(中国)有限公司 深蓝软件 迪米特法则(Law of Demeter LoD) 又叫做最少知识原则(Least Knowledge Principle,LKP),就是说,一个 对象应当对其他对象有尽可能少的了了解. 迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,与 1987年秋天由Ian Holland在美国东北大学为一个叫做迪米特 (Demeter)的项目设计提出的,因此叫做迪米特法则LIEB89LIEB86. 这条法则实际上是很多著名系统,比如火星登陆软件系统,木星的欧罗 巴卫星轨道飞船的软件系统的指导设计原则. 没有任何一个其他的OO设计原则象迪米特法则这样有如此之多的表 述方式,如下几种: (1)只与你直接的朋友们通信(Only talk to your immediate friends) (2)不要跟“陌生人“说话(Dont talk to strangers) (3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些 本单位密切相关的软件单位. 就是说,如果两个类不必彼此直接通信,那么这两个类就不应当发生直 接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话 ,可以通过第三者转发这个调用。 凌宁 微软(中国)有限公司 深蓝软件 合成/聚合复用原则( Composite/Aggregate Reuse Principle或CARP) 定义: 在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分 ;新的对象通过向这些对象的委派达到复用这些对象的目的。应首先 使用合成/聚合,合成/聚合则使系统灵活,其次才考虑继承,达到复 用的目的。而使用继承时,要严格遵循里氏代换原则。有效地使用继 承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建 、维护时的难度及系统的复杂度。如果两个类是“Has-a”关系应使用 合成、聚合,如果是“Is-a”关系可使用继承。“Is-A”是严格的分类学意 义上定义,意思是一个类是另一个类的“一种”。而“Has-A”则不同,它 表示某一个角色具有某一项责任。 凌宁 微软(中国)有限公司 深蓝软件 什么是合成?什么是聚合? 合成(Composition)和聚合(Aggregation)都是关联( Association)的特殊种类。聚合表示整体和部分的关系, 表示“拥有”。如奔驰S360汽车,对奔驰S360引擎、奔驰 S360轮胎的关系是聚合关系,离开了奔驰S360汽车,引 擎、轮胎就失去了存在的意义。在设计中, 聚合不应该频 繁出现,这样会增大设计的耦合度。合成则是一种更强的 “拥有”,部分和整体的生命周期一样。合成的新的对象完 全支配其组成部分,包括它们的创建和湮灭等。一个合成 关系的成分对象是不能与另一个合成关系共享的。换句话 说,合成是值的聚合(Aggregation by Value),而一般 说的聚合是引用的聚合(Aggregation by Reference)。 明白了合成和聚合关系,再来理解合成/聚合原则应该就 清楚了,要避免在系统设计中出现,一个类的继承层次超 过3层,则需考虑重构代码,或者重新设计结构。当然最 好的办法就是考虑使用合成/聚合原则。 凌

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论