Robin's Blog

记录 积累 学习 成长

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

第二部分 面向对象的设计原则

1、面向对象的设计要解决的核心问题是如何同时提高软件的可维护性(Maintainability)和可复用性(Reuseability(LY注:关键难在同时,因为软件的可维护性和可复用性有时并不一致)

2、在孤立的设计模式的背后应当还有一些普遍性的设计指导原则。比如“开闭”原则,它们是隐藏在设计模式背后的,比设计模式本身更基本和单纯的设计思想。

3、本章讨论的OO设计原则是提高软件系统的可维护性和可复用性的指导性原则。不但现有的设计模式是这些指导原则的体现,对于没有现成设计模式的地方也同样适用。而且我们在设计系统、寻找新模式的时候这些原则提供指导作用。

3 软件的可维护性(Maintainability)与可复用性(Reuseability

软件的可维护性与可复用性是两个独立的目标,就像两只奔跑的兔子一样,并不总是方向一致的。因此,在支持可维护性的同时,提高可复用性,是一个核心的问题。

3.1 软件系统的可维护性

统计证明,软件维护的成本是原始开发的很多倍。

软件的维护

软 件的维护与家用电器不同,后者的维护成本只是保护或恢复某种功能所需的时间和资源,因此家用电器的维护成本远低于购买或开发成本。而软件的维护不仅包括清 除错误和缺陷,而且还要对已有性能进行扩充,满足新的设计要求。换言之,维护中的软件是一个不断再生的软件,像一个“不断带来新礼物的礼物”。拿电视来类 比,就好比你先购买了一个黑白小电视,随着要求的提高,将它改造为一个彩色大电视一样。

软件的维护就是软件的再生。好的设计,必须能允许新的设计要求以较为容易和平稳的方式加入已有的系统中去。可维护性好的系统,维护工作能够以容易、准确、安全和经济的方式进行。LY注:见过蜕皮的蝉吗?也许用自然界的生物来类比软件更为贴切。因为每个生物都是大自然杰出的设计。鸟儿要维护脱落的羽毛,蛇类要忍耐蜕皮。这些易于变化的部分被设计为相对集中独立的部分,使得其它部位最大限度地不受它们的影响,生物可以健康的成长。)

一个典型的软件生命周期

设 计师们根据最初的设计要求设计出了原型系统,然后由程序员实现为一个优美的产品。这时的系统优美纯洁。然后客户接踵而来的小小修改要求彻底毁了它。人们不 得不采取权宜之计来平衡新的要求与旧系统之间的矛盾。随着修改部分在系统中的权重逐渐增加,这个系统最终变成了一堆散发着臭味的垃圾。人们碰也不愿意碰 它。直到最后人们终于忍无可忍,带着解脱的心情将它抛弃,重新开发。然后,历史将会重演….

设计师的辩解

设计师们说,系统腐烂的原因是由于:

1.       用户变化无常,使得设计无法跟上快速的变化,原有的设计没有给未来的变化预留空间。

2.       维护设计师和原始设计师往往不是一个人,因此对原有系统的理解是否正确也是一个问题,即使原有的设计可以容纳变化,维护设计师也常常没有发现这一点。

3.       所有,新的改动总是以破坏原有意图和框架的方式加入进去。而且它是日积月累造成的,因此维护设计师不能形成自己的风格和设计框架。就像一堆补丁东拼西凑在一起。直到原有的系统设计毁坏殆尽,彻底腐烂。

我们要思考的是,软件系统的性能要求一定会变。我们能不能在最初的设计中为日后的变化留出空间呢?LY注:唯一不变的就是变化。随着时间变化和对软件系统的了解,用户的需求发生变化是最最最寻常不过的事情。这有时真的让人恼火,呵呵。不过这是我们必须面对的问题,而且,这不能作为借口。)

真正的原因

Rober C Martin指出,导致一个软件可维护性低的原因有四:过于僵硬(Rigidity)、过于脆弱(Fragility)、复用率低(Immobility)、黏度过高(Viscosity).

1)过于僵硬

一个系统做好之后,任何一个新功能的加入,都会影响许多模块,最终演变成几个模块的大改动。因此,一个系统一旦做好后,不敢加入任何新的功能。这就是系统的僵硬化。LY注:从加入新功能的角度看待系统。我认为造成这种现象的原因是系统的各个模块分隔的不好,或者它们之间的联系过于紧密。)

2)过于脆弱

与僵硬化同时存在的,是软件修改已有代码时非常脆弱。对一处的修改,往往造成不相干的地方发生故障。这种一碰就碎的情况,叫做系统的脆弱性。LY注:这是从修改旧系统的角度看的。造成这种现象的原因同上)

3)复用率低

复 用,就是指一个软件的组成部分,可以在同一个项目的不同地方甚至另一个项目中重复使用。程序员发现一部分代码、函数和模块所做的事情可以在新的模块、系统 中使用。但是他们发现这些东西依赖的东西太多,很难将其剥离开来。因此最好的方法是重新写。这种系统有复用率低的特点。

4)黏度过高

一 个改动可以以保留原始设计意图和原始设计框架方式进行,也可以以破坏原始意图和框架的方式进行。第一种对系统的未来有利,第二种是权宜之计,可以解决短期 问题,但是会牺牲中长期的利益。如果一个系统的设计使得维护总是采用第二种方式比第一种容易,那么就叫做黏度过高。这会诱使程序员采取不正确的维护方案。(LY注: 为什么叫做黏度过高呢?此处我的理解是如果一个系统原有的设计过于耦合凌乱,使得对任何一处的更改都变得非常困难。就像一堆粘合在一起的物品,单独剥离某 一个部分是不可能的,程序员必须对全盘进行考虑,因此复杂度大大加强,难于理解,难于维护。因此程序员只好在上面打丑陋的补丁。唉,你说程序员容易吗!其 实,任何一个程序员都想做出一流的东西来,对打补丁这种事厌恶透顶,如果时间允许,我们宁可从头开发一个优美的东西。)

设计的目标

好的系统应具有如下的性质:可扩展性(Extensibility)、灵活性(Flexibility)、可插入性(Pluggability)

1)可扩展性

新的功能应该很容易的加入到系统中而不影响到系统原有的其他方面。这就是可扩展性,与过于僵硬相对应。LY注:都是从加入新功能的角度考虑的)

2)灵活性

允许代码灵活修改,不会影响到其他的模块。这就是灵活性,与过于脆弱是相对应的。LY注:是从修改原有系统角度考虑的

3)可插入性

容易将一个类抽出去,将另一个有相同借口的类加进来。这就是可插入性。是黏度过高的反面。(LY注:可插入性使得程序员对系统的理解难度降低了。PS:我认为软件设计的所有原则都是为了减低复杂性而且提高人们对系统的理解能力。不知道这样理解是否正确。

为了满足这三项设计要求,关键是适当的提高软件的可维护性和可复用性LY注:适当这两个字很有意思。它说明我们不能片面地追求可维护性或是可复用性。我们得通盘考虑,权衡两者的利弊,做出取舍。

3.2 系统的可复用性(Reuse

复用又叫重用,是重复使用的意思。目前,一般软件的复用率并不高,尤其在国内。

复用的重要性

复用的好处:

1.       较高的生产效率。(以及随之而来的成本降低)

2.       较高的软件质量。(错误可以更快的被纠正)

3.       恰当的使用复用可以改善系统的可维护性。

传统的复用

1、  代码的剪贴复用:最初步的复用,每块代码分散在各处,独立的演变,仍需要针对每一块修改和测试。这种复用带来的好处很有限。

2、  算法的复用:在已有的成熟算法中选择一个比自己重新开发一个好的多。比如排序算法。这就是算法的复用。

3、  数据结构的复用:

比如队列、栈、链表,在实践中得到了大量的应用。

传统的复用种类各有优点也有各自的缺陷。LY注:有哪些优势和缺陷呢?)

可维护性与复用的关系

传统复用方案的致命缺陷是复用常常以破坏可维护性为代价的。

因此我们要支持可维护性的复用,即在保持甚至提高系统的可维护性的同时,实现系统的复用。LY注: 旧有复用方式的缺陷是为了复用破坏了可维护性,或者说支持那些维护性差的复用。归根到底是因为,将不应该复用在一起的东西复用在了一起。设计上就出现了混 乱。良好的设计不应该出现这种后果。而现在我们仅考虑支持可维护性的复用。将相关的东西放在一起,不相关的部分即使看似相同可以复用,也不能这样做。因为 这种可复用其实是偶然的,以后系统一旦变化,这种所谓的“相同”就消失了。不知道我这个理解是否正确。)

面向对象设计的复用

在面向对象语言中,语言具有的良好特性,使得细节的复用变得简单成熟而不再是重点。复用的重点在于带有商业逻辑的抽象层次上。这是提高复用性同时保持和提高可维护性的关键。

抽象层次应是较为稳定的,是复用的重点。抽象层次的模块相对独立于具体层次。这样具体层次内部的变化就不会影响到抽象层次,抽象层次的模块复用会较为容易。

在面向对象设计中,可维护性复用以设计原则和设计模式为基础。

对可维护性的支持

针对三个目标,可扩展性、灵活性、可插入性。

1.       提高复用性,可以提高可扩展性。允许一个具有同样接口的新类替代旧类,是对抽象接口的复用。客户端依赖于抽象接口,而不是一个具体实现类,使得这个具体类可以被别的具体类替换,而不影响客户端。这提高了系统的可扩展性。

       系统可扩展性由“开-闭”原则、里氏代换原则、依赖倒转原则和组合/聚合复用原则所保证。LY:“开-闭”原则,是指一个软件实体应当对扩展开放,对修改关闭,即可以在不修改的情况下扩展。里氏代换原则,是指任何基类可以出现的地方,子类一定可以出现。依赖倒转原则,是指要依赖于抽象,不要依赖于实现。合成/聚合复用原则,是指要尽量使用合成/聚合,而不是继承关系来达到复用目的。

2.       提高复用性,可以提高灵活性。模块相对独立,通信尽可能少。这样当一个模块修改时,对别的模块的影响很小。

       这由“开-闭”原则、迪米特法则、接口隔离原则保证的。

LY注:迪米特法则,是指一个软件实体应该与尽可能少的其他实体发生相互作用。        接口隔离原则,是指应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口)

3.       提高复用性,可以提高系统的可插入性。当一部分不再满足需要时,可以将旧的部分拔出,新的部分插入。

       可插入性由“开-闭”原则、里氏代换原则、依赖倒转原则和组合/聚合复用原则所保证。

这样,在提高一个系统可维护性的同时,提高了这个系统的可复用性。具体的设计原则有:

l         “开-闭”原则

l         里氏代换原则

l         依赖倒转原则

l         接口隔离原则

l         组合/聚合复用原则

l         迪米特法则

这些原则首先是复用的原则,遵循这些原则可以有效提高系统的复用性,同时提高系统的可维护性。

学习设计模式对复用性与可维护性的帮助

设计模式是将OO设计理论应用到实践中的例子。设计模式分为创建模式、结构模式和行为模式。设计模式本身不能保证系统的可复用性和可维护性。但是通过学习这些设计模式的思想可以提高设计师的水平,加强与同行的交流,从而帮助设计师提高系统设计的可复用性和可维护性。LY注:对具体的问题而言,没有现成的解决方案。那些非常棒的东西不一定适合你的需要。你得自己动脑筋找到一个最适合自己的。不过,对杰作的学习会大大提高自己的功力。PS:学习这些模式其实是在学习大师们的思想,思维方式,不是吗?)

3.3 老子论“不武”

老子说“善为士者不武”。如果“士”是软件系统设计师,“武”是对系统的大规模修改,那么老子说的就可以解释为好的设计师不会在他设计的系统投入使用后再进行大规模的修改。老子的“不武”,便是软件设计中的“复用”(LY注: 原文出自《老子》第六十八章。“善为士者不武,善战者不怒,善胜敌者不与,善用人者为之下。是谓不争之德,是谓用人之力,是谓配天古之极。”译文:“善为 将帅者,不逞武勇;善于争战者,不涉忿怒;善于取胜者,不待交锋;善于用人者,态度谦下。这叫做不与客观当然相争的德行,这叫做任用人材的能力,这叫做符 合天古之极至之道。”)(LY注:好的设计其实是这样的。看来简单自然,让你觉得似乎没有费什么力气。正像道家说的,天似乎什么都没有做,但是万物就欣欣然的在其中生长。非常自然,非常和谐。因为遵循了内在的规律。软件设计的最高境界会不会就是让人感觉不出刀斧之痕,就像是自然生出来的一样呢?呵呵。PS:其实不止道家有“无为”的思想,其他诸子百家也有,扯远了,感兴趣的可以去看看。我该好好补补国文了,太多好书没读。

老 子说“天下有道,却走马以粪,天下无道,戎马生于效。”当天下有道(太平)时,好的跑马却在田间耕作;当天下无道(不太平)时,战马在战场上生出小驹。类 比的话,当一个软件系统是一个复用有道、易于维护的系统是,将新的性能加入到系统中去,或者对已有的系统修改都不困难,因此代码高手就无法发挥作用;而当 一个软件系统是一个设计低劣、可维护性很差的系统时,代码高手就必须连续作战,才能将新的性能加入到系统中,或者对其进行修改。(LY注:出自《老子》四十六章

这些先贤的思想应用到今天的软件开发中仍然很贴切。

posted on 2009-06-18 17:52  Robin99  阅读(609)  评论(0)    收藏  举报