电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

OO设计模式和设计原则


发布日期:2022/2/24
 

作者 Cherami

设计正在腐烂的征兆(Symptoms of Rotting Design)

有四个主要的征兆告诉我们该软件设计正在腐烂它们并不是互相独立的而是互相关联它们是过于僵硬过于脆弱不可重用性和粘滞性过高

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

过于脆弱Fragility Fragility 致使当软件改动时系统会在许多地方出错并且错误经常会发生在概念上与改动的地方没有联系的模块中这样的软件无法维护每一次维护都使软件变得更加难以维护(恶性循环)

不可重用性immobility immobility 致使我们不能重用在其它项目中或本项目中其它位置中的软件工程师发现将他想重用的部分分离出来的工作量和风险太大足以抵消他重用的积极性因此软件用重写代替了重用

粘滞性过高viscosity viscosity有两种形式设计的viscosity和环境的viscosity当需要进行改动时工程师通常发现有不止一个方法可以达到目的但是这些方法中一些会保留原有的设计不变而另外一些则不会(也就是说这些人是hacks)一个设计如果使工程师作错比作对容易得多那么这个设计的viscosity 就会很高

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

面向对象的类设计原则

开放关闭原则The Open Closed Principle (OCP)

A module should be open for extension but closed for modification一个模块应该只在扩展的时候被打开(暴露模块内部)在修改的时候是关闭的(模块是黑盒子)

在所有的面向对象设计原则中这一条最重要该原则是说我们应该能够不用修改模块的源代码就能更改模块的行为

动态多态性(Dynamic Polymorphism)

静态多态性(Static Polymorphism)

另外一种使用OCP的技术就是使用模板或范型如Listing LogOn函数不用修改代码就可以扩展出多种类型的modem OCP的体系结构目标(Architectural Goals of the OCP)

通过遵照OCP应用这些技术我们能创建不用更改内部代码就可以被扩展的模块这就是说在将来我们给模块增添新功能是只要增加新的代码而不用更改原先的代码 页使软件完全符合OCP可能是很难的但即使只是部分符合OCP整个软件的结构性能也会有很大的提高我们应该记住让变化不要波及已经正常工作的代码总是好的

Liskov 替换原则The Liskov Substitution Principle(LSP)

Subclasses should be substitutable for their base classes子类应该可以替换其基类

如下图所示Derived类应该能替换其Base类也就是说Base基类的一个用户User如果被传递给一个Devrived类而不是Base类作为参数也能正常的工作

依赖性倒置原则The Dependency Inversion Principle (DIP)

Depend upon Abstractions Do not depend upon concretions依赖抽象不要依赖具体

如果说OCP声明了OO体系结构的目的DIP则阐述了其主要机制依赖性倒置的策略就是要依赖接口或抽象函数或抽象类而不是依赖于具体的函数和类这条原则就是支持组件设计COMCORBAEJB等等的背后力量

依赖抽象Depending upon Abstractions

实现该原则十分简单设计中的每一个依赖都应该是接口抽象类不要依赖任何一个具体类

显然这样的限制比较严峻但是我们应该尽可能的遵守这条原则原因很简单具体的模块变化太多抽象的则变化少得多而且抽象是铰链在这些位置设计可以弯曲或者扩展而不用进行更改(OCP)

接口隔离原则The Interface Segregation Principle (ISP)

Many client specific interfaces are better than one general purpose interface多个和客户相关的接口要好于一个通用接口

ISP是另一条在底层支持组件如COM技术的原则没有它组件和类的易用性和重用性都会大打折扣该原则的实质很简单如果一个类有几个使用者与其让这个类载入所有使用者需要使用的所有方法还不如为每一个使用者创建一个特定的接口并让该类分别实现这些接口

包体系结构的原则Principles of Package Architecture

类是必不可少的但对于组织一个设计来说还不够粒度更大的包有助于此但是我们应该怎样协调类和包之间的从属关系?下面的三条原则都属于包聚合原则能对我们有所帮助

包聚合原则

发布重用等价原则The Release Reuse Equivalency Principle (REP)

重用的粒度就是发布的粒度The granule of reuse is the granule of release一个可重用的元件(组件一个类一组类等)只有在它们被某种发布(Release)系统管理以后才能被重用用户不愿意使用那些每次改动以后都要被强迫升级的元件因此即使开发者发布了可重用元件的新版本他也必须支持和维护旧版本这样才有时间让用户熟悉新版本

因此将什么类放在一个包中的判断标准之一就是重用并且因为包是发布的最小单元它们同样也是重用的最小单元体系结构师应该将可重用的类都放在包中

共同封闭原则The Common Closure Principle (CCP)

一起变化的类放在一起Classes that change together belong together一个大的开发项目通常分割成很多网状互联的包管理测试和发布这些包的工作可不是微不足道的工作在任何一个发布的版本中如果改动的包数量越多重建测试和部署也就会越多因此我们应该尽量减少在产品的发布周期中被改动的包的数量这就要求我们将一起变化的类放在一起(同一个包)

共同重用原则The Common Reuse Principle (CRP)

不一起重用的类不应该放在一起Classes that arent reused together should not be grouped together对一个包的依赖就是对包里面所有东西的依赖当一个包改变时这个包的所有使用者都必须验证是否还能正常运行即使它们所用到的没有任何改变也不行

比如我们就经常遇到操作系统需要升级当开发商发布一个新版本以后我们的升级是迟早的问题因为开发商将会不支持旧版本即使我们对新版本没有任何兴趣我们也得升级

如果把不一起使用的类放在一起同样的事情我们也会遇到一个和我们无关的类的改变也产生包的一个新版本我们被强迫升级和验证这个包是否影响正常的运行

包聚合原则之间的张力Tension between the Package Cohesion Principles

这三条原则实际上是互斥的它们不能被同时满足因为每一条原则都只针对某一方面只对某一部分人有好处REP和CRP都想重用元件的人有好处CCP对维护人员有好处CCP使得包有尽可能大的趋势(毕竟如果所有的类都属于一个包那么将只会有一个包变化)CRP尽量使得包更小

幸运的是包并不是一成不变的实际上在开发过程中包的转义和增删都是很正常的在项目开发的早期软件建筑师建立包的结构体系此时CCP占主导地位维护只是辅助在体系结构稳定以后软件建筑师会对包结构进行重构此时尽可能的运用REP和CRP从而最大的方便重用元件的人员

包耦合原则The Package Coupling Principles

下面三条原则主要关心包之间的关系

无依赖回路原则The Acyclic Dependencies Principle (ADP)

包与包之间的依赖不能形成回路The dependencies between packages must not form cycles因为包是发布的粒度人们倾向于节省人力资源所以工程师们通常只编写一个包而不是十几个包这种倾向由于包聚合原则被放大后来人们就将相关的类组成一组

因此工程师发现他们只会改动较少的几个包一旦这些改动完成他们就可以发布他们改动的包但是在发布前他们必须进行测试为了测试他们必须编译和连编他们的包所依赖的所有的包

依赖稳定原则(Stable Dependencies PrincipleSDP)

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

稳定性是什么?站在一个硬币上这稳定吗?很可能你说不然而除非被打扰硬币将保持那个位置很长时间硬币没有变化但是很难认为它是稳定的稳定性与需要改动所要做的工作量相关硬币不稳定是因为只需要很小的工作量就能把它翻过来换个角度桌子就要稳定得多

对于软件这说明什么?一个软件包很难被改动受很多因素影响代码大小复杂度透明度等等这些我们先不说可以肯定的一点是如果有很多其它的包依赖于一个软件包那么该软件包就难以改动一个包如果被许多其它包依赖那么该包是很稳定的因为这个包的任何一个改动都可能需要改动很多其它的包

稳定抽象原则( Stable Abstractions Principle SAP)

稳定的包应该是抽象包Stable packages should be abstract packages我们可以想象应用程序的包结构应该是一个互相联系的包的集合其中不稳定的包在顶端稳定的包在底部所有的依赖方向朝下那些顶端的包是不稳定而且灵活的但那些底部的包就很难改动这就导致一个两难局面我们想要将包设计为难以改动的吗?

明显地难以改动的包越多我们整个软件设计的灵活性就越差但是好像有一点希望解决这个问题位于依赖网络最底部的高稳定性的包的确难以改动但是如果遵从OCP这样的包并不难以扩展

上一篇:最简单的设计模式学习:Singleton模式

下一篇:使用Decorator模式实现日期选择组件(4)