软件的可维护性与可复用性
可维护性和可复用性是两个独立的目标,对面向对象的软件系统设计来说,在支持可维护性(Maintainability)的同时,提高系统的可复用性(Reuseability)是一个核心的问题。
软件系统的可维护性
维护中的软件是一个不断再生的软件,就像一个“不断带来新礼物的礼物”。
好的软件设计,允许新的设计要求以较为容易和平稳的方式加入到已有的系统中去,从而使这个系统能不断焕发青春。
一个可维护性较好的系统,允许维护工作能够以容易、准确、安全和经济的形式进行。
典型的软件生命周期
系统设计师拿到系统的设计要求,开始进行系统设计。
从存在于设计者们的头脑中,到存在于设计图纸上,然后变成一个原型系统,最后变成一个真实的、有血有肉、可以交付客户使用的成品。
交付客户运行后,客户会提出少许修改要求,这些要求都是在客户提出系统需求时遗忘的一些“小”问题。这带来的修改与现有系统的设计并不一定相容,设计师不得不采取一些权宜之计。
这些变更会越来越多,其中一些改变了系统的初衷。随着时间的流逝,系统变成无人愿意维护的烂码。
经过一段漫长的时间之后,在抱怨了软件系统的维护之昂贵之后,客户决定投入一笔资金,一个新的系统将被设计出来,而这个新系统将具有这个正在死去的系统的所有功能(还有一些新功能),并且取代这个已经腐烂的系统。
设计师的辩解
面对这人们的抱怨,系统设计师都有一套一模一样的辩解:用户要求的变化无常,使得系统设计无法跟上如此快速的变化。如果维护设计师与原始设计师不是同一组人员,就还会有很多与技术无关的辩解出来。
以此逻辑,一个系统的原始设计不可能预测系统的性能要求会发生什么样的变化,这就导致一个系统的设计无法与新的性能要求相容。这样一来,即便新的性能可以添加到系统中去,但是却不得不以某种破坏原始设计意图和设计框架的方式加进去。
由于这些改动都是以积累的方式进行的,因此,维护设计师无法形成自己的设计意图和新的设计框架,所以这些破坏都不会带来新的意图和框架的建设,而只能是一些东拼西凑的权宜之计。
这样的破坏式增强功能越来越多,以至于最后原始的设计意图和设计框架已经彻底被这些没有总体考虑和固定规律的东西取代,系统就这样腐烂了。
一个系统的设计应当为日后的变化流出足够的空间。
真正的原因
导致软件设计的可维护性较低的真正原因:过于僵硬(Rigidity)、过于脆弱(Fragility)、复用率低(Immobility)、粘度过高(Viscosity)。
过于僵硬
模块依赖严重,一石激起千层浪,一个模块的代入,波及很多其他模块,势必造成难度和复杂度倍增,让项目经理不敢轻易尝试加入新功能。
过于脆弱
无独有偶,牵一发而动全身,一碰就会碎,修改一个地方,会导致看上去毫无关系的其他地方瘫痪。尽管变更之前,设计师会竭尽所能预测可能涉及的故障点,但是在改完之前,系统的原始设计师甚至无法确切预测可能波及的地方。
复用率低
复用,是局部程序的共享。
不过一段代码、函数、模块所做的事情是可以在新模块、或新系统中使用的时候,这些已有的代码却依赖于一大堆其他的东西,以至于很难将它们分开。最后,最好的办法是不去“碰”这些已有的东西,而是重写自己的代码,以源代码复制粘贴的原始方式,冗余解决问题。
粘度过高
有时候,一个变更可以以保持原始设计意图和原始设计框架不变的方式进行(无损变更),也可以以破坏原始意图和框架的方式进行(有损变更)。前者无疑会对系统的未来有利,后者则是权宜之计,可以解决短期问题,但会牺牲中长期利益。
如果第二种方法比第一种方法容易的多的话,程序员就可能会牺牲中长期的利益,采取权宜之计;在模块中搭建一个短路桥,或者在一个通用的逻辑中制造一个特例,以便解决眼前的需要。
一个系统设计,如果总是使得第二种方法比第一种方法容易,即为粘度过高。一个粘度过高的系统会诱使维护它的程序员采取错误的维护方案,并惩罚采取正确维护方案的程序员。
设计的目标
一个好的系统设计应该如下性质:
可扩展性(Extensibility)、灵活性(Flexibility)、可插入性(Pluggability)。
可扩展性
新功能可以很容易地加入系统中去(增加不影响原有功能,并且删除此功能也不影响原有系统的完整性)。
灵活性
允许代码修改平稳地发生,不会波及其他模块。各模块具备良好的隔离性。
可插入性
可以很容易抽取一个类,同时将另一个同样接口的类加进来。
系统的可复用性
复用重要性
较高的生产效率;较高的软件质量;恰当使用复用可以改善系统的可维护性。
传统的复用
代码的剪贴复用
算法的复用
数据结构的复用
可维护性与复用的关系
传统的复用方案的一个致命缺陷是复用常常是以破坏可维护性为代价的。
因此,支持可维护性的复用,在保持甚至提高系统的可维护性的同时,实现系统的复用。
面向对象设计的复用
面向对象的语言中,数据的抽象化、继承、封装和多态性使得一个系统可以在更高的层次上提供可复用性。
数据的抽象化和继承关系可以使得概念和定义可以复用;
多态性使得实现和应用可以复用;
抽象化和封装可以保持和促进系统的可维护性。
商业逻辑的抽象层次的复用是提高复用性的同时提高可维护性的关键。
抽象层次是一个应用系统战略性判断和决定的地方,是稳定的,是复用的重点。具体层次的模块的内部变化不会影响到抽象层次的结构。
面向对象的设计原则里,可维护性复用是以设计原则和设计模式为基础的。
对可维护性的支持
恰当地提高系统的可复用性,可以提高系统的可扩展性。
允许一个具有同样接口的类代替旧的类,是对抽象接口 的复用。客户端依赖于一个抽象的接口,而不是一个具体实现类。
系统的可扩展性是由“开-闭”原则、里氏代换原则、依赖倒转原则和组合/聚合复用原则保证的。
恰当地提高系统的可复用性,可以提高系统的灵活性。在一个设计得当的系统中,每个模块都相对于其他模块独立存在,并只保持与其他模块尽可能少的通信。一个变更,不会传递到其他。
系统的灵活性是由“开-闭”原则、迪米特法则、接口隔离原则保证的。
恰当地提高系统的可复用性,可以提高系统的可插入性。在一个符合“开-闭”原则的系统中,抽象层封装了与商业逻辑有关的重要行为,这些行为的具体实现由实现层给出。当一个实现类不再满足需要,需要以另一个实现类取代的时候,系统的设计可以保证旧的类可以被“拔出(Unplug)”,新的类可以被“插入(Plug)”。
系统的可插入性是由“开-闭”原则、里氏代换原则、组合/聚合复用原则以及依赖倒转原则保证的。
声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 嗅谱网
转载请注明:转自《软件的可维护性与可复用性》
本文地址:http://www.xiupu.net/archives-7654.html
关注公众号:
微信赞赏
支付宝赞赏