OO设计模式和设计原则

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.1 设计正在“腐烂”的征兆(Symptoms of Rotting Design)
有四个主要的征兆告诉我们该软件设计正在“腐烂”中。

它们并不是互相独立的,而是互相关联,它们是过于僵硬、过于脆弱、不可重用性和粘滞性过高。

1. 过于僵硬Rigidity Rigidity 致使软件难以更改,每一个改动都会造成一连串的互相依靠的模块的改动,项目经理不敢改动,因为他永远也不知道一个改动何时才能完成。

2. 过于脆弱Fragility Fragility 致使当软件改动时,系统会在许多地方出错。

并且错误经常会发生在概念上与改动的地方没有联系的模块中。

这样的软件无法维护,每一次维护都使软件变得更加难以维护。

(恶性循环)
3. 不可重用性immobility immobility 致使我们不能重用在其它项目中、或本项目中其它位置中的软件。

工程师发现将他想重用的部分分离出来的工作量和风险太大,足以抵消他重用的积极性,因此软件用重写代替了重用。

4. 粘滞性过高viscosity viscosity有两种形式:设计的viscosity和环境的viscosity.当需要进行改动时,工程师通常发现有不止一个方法可以达到目的。

但是这些方法中,一些会保留原有的设计不变,而另外一些则不会(也就是说,这些人是hacks)。

一个设计如果使工程师作错比作对容易得多,那么这个设计的viscosity 就会很高。

环境的viscosity高是指开发环境速度很慢且效率很低。

2 面向对象的类设计原则
2.1 开放关闭原则The Open Closed Principle (OCP)
A module should be open for extension but closed for modification.一个模块应该只在扩展的时候被打开(暴露模块内部),在修改的时候是关闭的(模块是黑盒子)。

在所有的面向对象设计原则中,这一条最重要。

该原则是说:我们应该能够不用修改模块的源代码,就能更改模块的行为。

2.1.1 动态多态性(Dynamic Polymorphism)
2.1.2 静态多态性(Static Polymorphism)
另外一种使用OCP的技术就是使用模板或范型,如Listing 2-3.LogOn函数不用修改代码就可以扩展出多种类型的modem. 2.1.3 OCP的体系结构目标(Architectural Goals of the OCP)
通过遵照OCP应用这些技术,我们能创建不用更改内部代码就可以被扩展的模块。

这就是说,在将来我们给模块增添新功能是,只要增加新的代码,而不用
更改原先的代码。

第 3 页,共 17 页使软件完全符合OCP可能是很难的,但即使只是部分符合OCP,整个软件的结构性能也会有很大的提高。

我们应该记住,让变化不要波及已经正常工作的代码总是好的。

2.2 Liskov 替换原则The Liskov Substitution Principle(LSP)
Subclasses should be substitutable for their base classes.子类应该可以替换其基类。

如下图2-14所示。

Derived类应该能替换其Base类。

也就是说,Base基类的一个用户User如果被传递给一个Devrived类而不是Base类作为参数,也能正常的工作。

2.3 依赖性倒置原则The Dependency Inversion Principle (DIP)1
Depend upon Abstractions. Do not depend upon concretions.依赖抽象,不要依赖具体。

如果说OCP声明了OO体系结构的目的,DIP则阐述了其主要机制。

依赖性倒置的策略就是要依赖接口、或抽象函数、或抽象类,而不是依赖于具体的函数和类。

这条原则就是支持组件设计、COM、CORBA、EJB等等的背后力量。

2.3.1 依赖抽象Depending upon Abstractions.
实现该原则十分简单。

设计中的每一个依赖都应该是接口、抽象类,不要依赖任何一个具体类。

显然这样的限制比较严峻,但是我们应该尽可能的遵守这条原则。

原因很简单,具体的模块变化太多,抽象的则变化少得多。

而且,抽象是“铰链”点,在这些位置,设计可以弯曲或者扩展,而不用进行更改(OCP)。

2.4 接口隔离原则The Interface Segregation Principle (ISP)
‘Many client specific interfaces are better than one general purpose interface多个和客户相关的接口要好于一个通用接口。

ISP是另一条在底层支持组件如COM技术的原则。

没有它,组件和类的易用性和重用性都会大打折扣。

该原则的实质很简单:如果一个类有几个使用者,与其让这个类载入所有使用者需要使用的所有方法,还不如为每一个使用者创建一个特定的接口,并让该类分别实现这些接口。

3 包体系结构的原则Principles of Package Architecture
类是必不可少的,但对于组织一个设计来说还不够,粒度更大的包有助于此。

但是我们应该怎样协调类和包之间的从属关系?下面的三条原则都属于包聚合原则,能对我们有所帮助。

3.1 包聚合原则
3.1.1 发布重用等价原则The Release Reuse Equivalency Principle (REP)1
重用的粒度就是发布的粒度。

The granule of reuse is the granule of release.一个可重用的元件(组件、一个类、一组类等),只有在它们被某种发布(Release)系统管理以后,才能被重用。

用户不愿意使用那些每次改动以后都要被强迫升级的元件。

因此,即使开发者发布了可重用元件的新版本,他也必须支持和维护旧版本,这样才有时间让用户熟悉新版本。

因此,将什么类放在一个包中的判断标准之一就是重用,并且因为包是发布的最小单元,它们同样也是重用的最小单元。

体系结构师应该将可重用的类都放在包中。

3.1.2 共同封闭原则The Common Closure Principle (CCP)2
一起变化的类放在一起。

Classes that change together, belong together.一个大的开发项目通常分割成很多网状互联的包。

管理、测试和发布这些包的工作可不是微不足道的工作。

在任何一个发布的版本中,如果改动的包数量越多,重建、测试和部署也就会越多。

因此我们应该尽量减少在产品的发布周期中被改动的包的数量,这就要求我们将一起变化的类放在一起(同一个包)。

3.1.3 共同重用原则The Common Reuse Principle (CRP)3
不一起重用的类不应该放在一起。

Classes that aren‘t reused together should not be grouped together.对一个包的依赖就是对包里面所有东西的依赖。

当一个包改变时,这个包的所有使用者都必须验证是否还能正常运行,即使它们所用到的没有任何改变也不行。

比如我们就经常遇到操作系统需要升级。

当开发商发布一个新版本以后,我们的升级是迟早的问题,因为开发商将会不支持旧版本,即使我们对新版本没有任何兴趣,我们也得升级。

如果把不一起使用的类放在一起,同样的事情我们也会遇到。

一个和我们无关的类的改变也产生包的一个新版本,我们被强迫升级和验证这个包是否影响正常的运行。

3.1.4 包聚合原则之间的张力Tension between the Package Cohesion Principles
这三条原则实际上是互斥的。

它们不能被同时满足,因为每一条原则都只针对某一方面,只对某一部分人有好处。

REP和CRP都想重用元件的人有好处,CCP 对维护人员有好处。

CCP使得包有尽可能大的趋势(毕竟,如果所有的类都属于一个包,那么将只会有一个包变化);CRP尽量使得包更小。

幸运的是,包并不是一成不变的。

实际上,在开发过程中,包的转义和增删都是很正常的。

在项目开发的早期,软件建筑师建立包的结构体系,此时CCP
占主导地位,维护只是辅助。

在体系结构稳定以后,软件建筑师会对包结构进行重构,此时尽可能的运用REP和CRP,从而最大的方便重用元件的人员。

3.2 包耦合原则The Package Coupling Principles.
下面三条原则主要关心包之间的关系。

3.2.1 无依赖回路原则The Acyclic Dependencies Principle (ADP)1
包与包之间的依赖不能形成回路。

The dependencies between packages must not form cycles.因为包是发布的粒度。

人们倾向于节省人力资源,所以工程师们通常只编写一个包而不是十几个包。

这种倾向由于包聚合原则被放大,后来人们就将相关的类组成一组。

因此,工程师发现他们只会改动较少的几个包,一旦这些改动完成,他们就可以发布他们改动的包。

但是在发布前,他们必须进行测试。

为了测试,他们必须编译和连编他们的包所依赖的所有的包。

3.2.2 依赖稳定原则(Stable Dependencies Principle,SDP)
朝稳定的方向依赖Depend in the direction of stability.虽然这条原则看起来很明显,但是关于这方面还是有很多需要说明的地方,稳定性并不一定为大家所了解。

稳定性是什么?站在一个硬币上,这稳定吗?很可能你说不。

然而,除非被打扰,硬币将保持那个位置很长时间。

硬币没有变化,但是很难认为它是稳定的。

稳定性与需要改动所要做的工作量相关。

硬币不稳定是因为只需要很小的工作量就能把它翻过来。

换个角度,桌子就要稳定得多。

对于软件这说明什么?一个软件包很难被改动受很多因素影响:代码大小、复杂度、透明度等等。

这些我们先不说,可以肯定的一点是,如果有很多其它的包依赖于一个软件包,那么该软件包就难以改动。

一个包如果被许多其它包依赖,那么该包是很稳定的,因为这个包的任何一个改动都可能需要改动很多其它的包。

3.2.3 稳定抽象原则( Stable Abstractions Principle ,SAP)
稳定的包应该是抽象包。

Stable packages should be abstract packages.我们可以想象应用程序的包结构应该是一个互相联系的包的集合,其中不稳定的包在顶端,稳定的包在底部,所有的依赖方向朝下。

那些顶端的包是不稳定而且灵活的,但那些底部的包就很难改动。

这就导致一个两难局面:我们想要将包设计为难以改动的吗?
明显地,难以改动的包越多,我们整个软件设计的灵活性就越差。

但是好像有一点希望解决这个问题,位于依赖网络最底部的高稳定性的包的确难以改动,但是如果遵从OCP,这样的包并不难以扩展。

相关文档
最新文档