面向对象的设计法则系列--BobTarr.doc_第1页
面向对象的设计法则系列--BobTarr.doc_第2页
面向对象的设计法则系列--BobTarr.doc_第3页
面向对象的设计法则系列--BobTarr.doc_第4页
面向对象的设计法则系列--BobTarr.doc_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

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

文档简介

WebScope Group Forum /forum/index.jsp面向对象的设计法则系列Bob Tarr 著版本: 0.1.0 编写: outmyth 日期: 2002-11-20 页数: 共 19 页 WebScope小组 Copyright (C) 2002-2003 WebScope Group,All Rights Reserved。修 改 说 明日 期版 本 号摘 要作 者2002-11-200.1.0创建outmyth目 录n 1:优先使用(对象)组合,而非(类)继承3组合的优点和缺点4继承4继承的优点和缺点4Coad规则5继承/组合示例15继承/组合示例27继承/组合示例38继承/组合示例49继承/组合总结9n 2:针对接口编程,而非(接口的)实现10接口10实现继承和接口继承10接口的好处11接口实例12n 3:开放封闭法则(OCP)12开放封闭法则12OCP示例13相关法则15(a)单选法则15n 4:Liskov替换法则(LSP)16Liskov替换法则16LSP示例17总结19【译者注】本文主要是以Bob Tarr的文章作为翻译的主线(黑色),GOF(紫色),C+ View上翻译Robert Martin的文章(褐色)和Webscope(蓝色和绿色)上的文章以及译者的见解为辅。1:优先使用(对象)组合,而非(类)继承 Favor Composition Over Inheritance n 组合(对象)组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。将功能委托给所组合的一个对象,从而获得新功能。有些时候也称之为“聚合”(aggregation)或“包容”(containment),尽管有些作者对这些术语赋予了专门的含义例如:聚合:一个对象拥有另一个对象或对另一个对象负责(即一个对象包含另一个对象或是另一个对象的一部分),并且聚合对象和其所有者具有相同的生命周期。(译者注:即所谓的“同生共死”关系)(可参见GOF的Design Patterns: Elements of Reusable Object-Oriented Software的引言部分)包容:一种特殊类型的组合,对于其它对象而言,容器中的被包含对象是不可见的,其它对象仅能通过容器对象来访问被包含对象。(Coad)n 包含可以通过以下两种方式实现:F 根据引用(By reference)F 根据值(By value)n C允许根据值或引用来实现包含。n 但是在Java中,一切皆为对象的引用!组合的优点和缺点n 优点:F 容器类仅能通过被包含对象的接口来对其进行访问。F “黑盒”复用,因为被包含对象的内部细节对外是不可见。F 封装性好。F 实现上的相互依赖性比较小。(译者注:被包含对象与容器对象之间的依赖关系比较少)F 每一个类只专注于一项任务。F 通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。n 缺点:F 从而导致系统中的对象过多。F 为了能将多个不同的对象作为组合块(composition block)来使用,必须仔细地对接口进行定义。继承n (类)继承是一种通过扩展一个已有对象的实现,从而获得新功能的复用方法。n 泛化类(超类)可以显式地捕获那些公共的属性和方法。n 特殊类(子类)则通过附加属性和方法来进行实现的扩展。继承的优点和缺点n 优点:F 容易进行新的实现,因为其大多数可继承而来。F 易于修改或扩展那些被复用的实现。n 缺点:F 破坏了封装性,因为这会将父类的实现细节暴露给子类。F “白盒”复用,因为父类的内部细节对于子类而言通常是可见的。F 当父类的实现更改时,子类也不得不会随之更改。F 从父类继承来的实现将不能在运行期间进行改变。Coad规则仅当下列的所有标准被满足时,方可使用继承:n 子类表达了“是一个的特殊类型”,而非“是一个由所扮演的角色”。n 子类的一个实例永远不需要转化(transmute)为其它类的一个对象。n 子类是对其父类的职责(responsibility)进行扩展,而非重写或废除(nullify)。n 子类没有对那些仅作为一个工具类(utility class)的功能进行扩展。n 对于一个位于实际的问题域(Problem Domain)的类而言,其子类特指一种角色(role),交易(transaction)或设备(device)。继承/组合示例1n “是一个的特殊类型”,而非“是一个由所扮演的角色”F 失败。乘客是人所扮演的一种角色。代理人亦然。n 永远不需要转化F 失败。随着时间的发展,一个Person的子类实例可能会从Passenger转变成Agent,再到Agent Passenger。n 扩展,而非重写和废除F 通过。n 不要扩展一个工具类F 通过。n 在问题域内,特指一种角色,交易或设备F 失败。Person不是一种角色,交易或设备。继承并非适用于此处!使用组合进行挽救!继承/组合示例2n “是一个的特殊类型”,而非“是一个由所扮演的角色”F 通过。乘客和代理人都是特殊类型的人所扮演的角色。n 永远不需要转化F 通过。一个Passenger对象将保持不变;Agent对象亦然。n 扩展,而非重写和废除F 通过。n 不要扩展一个工具类F 通过。n 在问题域内,特指一种角色,交易或设备F 通过。PersonRole是一种类型的角色。继承适用于此处!继承/组合示例3n “是一个的特殊类型”,而非“是一个由所扮演的角色”F 通过。预订和购买都是一种特殊类型的交易。n 永远不需要转化F 通过。一个Reservation对象将保持不变;Purchase对象亦然。n 扩展,而非重写和废除F 通过。n 不要扩展一个工具类F 通过。n 在问题域内,特指一种角色,交易或设备F 通过。是一种交易。继承适用于此处!继承/组合示例4n “是一个的特殊类型”,而非“是一个由所扮演的角色”F 失败。预订不是一种特殊类型的observable。n 永远不需要转化F 通过。一个Reservation对象将保持不变。n 扩展,而非重写和废除F 通过。n 不要扩展一个工具类F 失败。Observable就是一个工具类。n 在问题域内,特指一种角色,交易或设备F 不适用。Observable是一个工具类,并非一个问题域的类。继承并非适用于此处!继承/组合总结n 组合与继承都是重要的重用方法n 在OO开发的早期,继承被过度地使用n 随着时间的发展,我们发现优先使用组合可以获得重用性与简单性更佳的设计n 当然可以通过继承,以扩充(enlarge)可用的组合类集(the set of composable classes)。n 因此组合与继承可以一起工作n 但是我们的基本法则是:优先使用对象组合,而非(类)继承 Favor Composition Over Inheritance 2:针对接口编程,而非(接口的)实现 Program To An Interface, Not An Implementation 接口n 接口是一个对象在对其它的对象进行调用时所知道的方法集合。(译者注:GOF也给出了接口的定义,即对象接口描述了该对象所能接受的全部请求的集合。可参见GOF的Design Patterns: Elements of Reusable Object-Oriented Software的引言部分)n 一个对象可以有多个接口(实际上,接口是对象所有方法的一个子集)n 类型是对象的一个特定的接口。n 不同的对象可以具有相同的类型,而且一个对象可以具有多个不同的类型。(译者注:GOF论述接口与类型之间的关系,即若一个对象接受“Window”接口所定义的所有操作请求,则我们就说该对象具有“Window”类型。对象接口的某部分可以用某个类型来刻画,而其它部分则可用其它类型刻画(也就是说类型是组成和划分接口的基本单位)。两个类型相同的对象只需要共享它们的部分接口。接口可以包含其它接口作为子集:当一个类型的接口包含另一个类型的接口时,我们就说它是另一个类型的子类型(subtype),另一个类型称之为它的超类型(supertype)。我们常说子类型继承了它的超类型的接口。可参见GOF的Design Patterns: Elements of Reusable Object-Oriented Software的引言部分。)n 一个对象仅能通过其接口才会被其它对象所了解。n 某种意义上,接口是以一种非常局限的方式,将“是一种”表达为“一种支持该接口的”。n 接口是实现插件化(pluggability)的关键(译者注:对象的类和对象的类型的差别:一个对象的类定义了对象是怎样实现的,同时也定义了对象的内部状态和对象作的实现。但是对象的类型只与其接口有关。接口即对象能响应的请求的集合。当然对象的类和类型是有系统关系的。因为类定义了对象所能执行的操作。也定义了对象的类型。当我们说一个对象是一个类的实例时,即指该对象支持类所定义的接口。可参见GOF的Design Patterns: Elements of Reusable Object-Oriented Software的引言部分。)实现继承和接口继承n 实现继承(类继承):一个对象的实现是根据另一个对象的实现来定义的。n 接口继承(子类型化):描述了一个对象可在什么时候被用来替代另一个对象。n C+的继承机制既指类继承,又指接口继承。n C+通过继承纯虚类来实现接口继承。n Java对接口继承具有单独的语言构造方式Java接口。n Java接口构造方式更加易于表达和实现那些专注于对象接口的设计。(译者注:在实际开发中我们也许会有疑问,即接口继承和类继承有什么具体的不同?,那么我们应该什么时候使用接口继承,什么时候使用类继承呢?其实类继承更靠近我们一般常说的“继承”的概念,在基类中封装了公有的属性和方法,从而省去了子类内的重复代码;而接口继承正如LSP法则所论述的那样,子类对象(接口的具体实现)对象之所以能够替代父类对象(接口)的位置,主要是因为其具有父类的所有行为,并且如果子类对象出现在父类对象的位置上,它仅能表达父类所定义的行为,而不能表达自己所特有的行为。所以说接口定义的是一组通用的,更加抽象的行为集合,其所针对的仅为方法,而非属性。在继承体系中,不同的实现方法正是需要使用接口来定义的,即所谓的“用接口来封装变化”。不同的各个子类的实现方法在变化速率上是很快的,如果其它模块直接调用这些具体的子类的实现方法是非常困难的,因此需要对这些子类的不同实现进行封装(使用接口),这样其它模块便可使用接口来调用它们,而不必在调用时就知道具体的是哪个具体的实现。同时接口继承和类继承都是属于ISA关系(继承)的范畴,只不过类继承更多的是针对属性和方法(往往出于代码重用的需要),而接口继承则完全是针对功能(行为)来进行定义的。接口除了表达ISA关系外,其最主要的作用就是对行为进行抽象,特别是那些在各个子类中具体实现各不相同的行为,因为抽象的接口相对而言变化的速率会很慢,这样其它模块才能在抽象的层次上对其进行依赖并完成相应间的调用,这一点对于框架的设计尤为重要,比如在Java Collection Framework上就有非常好的诠释。Java便为这两种方式提供了语言层面上的支持:接口继承(interface)和类继承(extend,abstract);而C+则是使用纯虚类来表示接口的概念,但是并没有具体明确这两种方式。接口可用来封装变化,将对象的接口(变化慢)与其功能实现(变化快)相分离。该部分也会涉及到“多态”(动态绑定)这个主题)接口的好处n 优点:F Client不必知道其使用对象的具体所属类。F 一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。F 对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。F 松散藕合(loosens coupling)。F 增加了重用的可能性。F 提高了(对象)组合的机率,因为被包含对象可以是任何实现了一个指定接口的类。n 缺点:F 设计的复杂性略有增加接口实例n 该方法是指其它的一些类可以进行交通工具的驾驶,而不必关心其实际上是(汽车,轮船,潜艇或是其它任何实现了IManeuverabre的对象)。3:开放封闭法则(OCP)软件组成实体应该是可扩展的,但是不可修改的。 Software Entities Should Be Open For Extension, Yet Closed For Modification 开放封闭法则n 开放-封闭法则认为我们应该试图去设计出永远也不需要改变的模块。n 我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改。n 符合OCP的模块需满足两个标准:F 可扩展,即“对扩展是开放的”(Open For Extension)模块的行为可以被扩展,以需要满足新的需求。F 不可更改,即“对更改是封闭的”(Closed for Modification)模块的源代码是不允许进行改动的。n 我们能如何去做呢?F 抽象(Abstraction)F 多态(Polymorphism)F 继承(Inheritance)F 接口(Interface)n 一个软件系统的所有模块不可能都满足OCP,但是我们应该努力最小化这些不满足OCP的模块数量。n 开放封闭法则是OO设计的真正核心。(这也是OO设计的目标。)n 符合该法则便意味着最高等级的复用性(reusability)和可维护性(maintainability)。OCP示例n 考虑下面某类的方法:n 以上函数的工作是在制订的部件数组中计算各个部件价格的总和。n 若Part是一个基类或接口且使用了多态,则该类可很容易地来适应新类型的部件,而不必对其进行修改。n 其将符合OCPn 但是在计算总价格时,若财务部颁布主板和内存应使用额外费用,则将如何去做。n 下列的代码是如何来做的呢?n 这符合OCP吗?n 当每次财务部提出新的计价策略,我们都不得不要修改totalPrice()方法!这并非“对更改是封闭的”。显然,策略的变更便意味着我们不得不要在一些地方修改代码的,因此我们该如何去做呢?n 为了使用我们第一个版本的totalPrice(),我们可以将计价策略合并到Part的getPrice()方法中。n 这里是Part和ConcretePart类的示例:n 但是现在每当计价策略发生改变,我们就必须修改Part的每个子类!n 一个更好的思路是采用一个PricePolicy类,通过对其进行继承以提供不同的计价策略:n 看起来我们所做的就是将问题推迟到另一个类中。但是使用该解决方案,我们可通过改变Part对象,在运行期间动态地来设定计价的策略。n 另一个解决方案是使每个ConcretePart从数据库或属性文件中获取其当前的价格。相关法则(a) 单选法则单选法则(the Single Choice Principle)是OCP的一个推论。单选法则:无论在什么时候,一个软件系统必须支持一组备选项,理想情况下,在系统中只能有一个类能够知道整个的备选项集合。4:Liskov替换法则(LSP)Liskov替换法则使用指向基类(超类)的引用的函数,必须能够在不知道具体派生类(子类)对象类型的情况下使用它们。 Function Thar Use Referennces To Base(Super) Classes Must Be Able To Use Objects Of Derived(Sub) Classes Without Knowing It 以下文字是由LSP的提出者Barbara Liskov所给出的LSP的定义:这里需要如下的替换性质:若对于每一个类型S的对象o1,都存在一个类型T的对象o2,使得在所有针对T编写的程序P中,用o1替换o2后,程序P的行为功能不变,则S是T的子类型。What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.【译者注:Barbara Liskov的原文中这段话的前面还有一部分:A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.意思是说,类型层次由子类型和超类型组成,直觉告诉我们,子类型的含义就是该类型的对象提供了另外一个类型(超类型)的对象的所有行为功能,并有所扩充。】 请参见C+ view第3期第40页。n 显而易见,Liskov替换法则(LSP)是根据我所熟知的“多态”而得出的。n 例如:n 方法drawShape应该可与Sharp超类的任何子类一起工作(或者,若Sharp为Java接口,则该方法可与任何实现了Sharp接口的类一起工作)n 但是当我们在实现子类时必须要谨慎对待,以确保我们不会无意中违背了LSP。n 若一个函数未能满足LSP,那么可能是因为它显式地引用了超类的一些或所有子类。这样的函数也违背了OCP,因为当我们创建一个新的子类时,会不得不进行代码的修改。LSP示例n 考虑下面Rectangle类:n 现在,Square类会如何呢?显然,一个正方形是一个四边形,因此Square类应该从Rectangle类派生而来,对否?让我们看一看!n 观察可得:F 正方形不需要将高和宽都作为属性,但是总之它将继承自Rectangle。因此,每一个Square对象会浪费一

温馨提示

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

评论

0/150

提交评论