1。软件的可维护性与可复用性
软件的维护就是软件的再生。系统的设计目标: 可扩展性,灵活性,可插入性。
可扩展性: 新的功能很容易集成到现有的系统中去,而不影响到系统的其他模块。
灵活性: 允许代码修改平稳的发生。当修改一处时不至于影响到另一处,这样可以缩小维护的代价。
可插入性: 容易用一个类替换已经存在的类。只要接口一致,更改实现类不影响类的使用者。
软件的复用可以提高软件的生产率,并且恰当的复用可以提高软件的可维护性。
在以前,复用主要是代码,函数,结构的复用,而现在复用主要针对类,接口,组件等等。但是复用并不一定会保证软件的可维护性。不能因为代码的重复等原因就复用,需要根据具体的情况来分析。要想通过复用来加强系统的可维护性,必须保证复用是支持可维护性的复用。
下面的一些设计原则可以用来指导实践。
2。开-闭 原则(OCP)
开闭原则是面向对象可复用的基石。它主要指:一个软件实体对扩展开放,对修改关闭。在设计一个模块的时候,应当是这个模块在不被修改的前提下被扩展。满足这个原则的系统在一个较高层次上实现了复用,也是易于维护的。
那如何才能满足开闭原则呢?抽象化是关键。要区分开抽象层和实现层。在一个软件系统中,抽象层应该是相对稳定的,而实现层是可以改变和扩展的。
开闭原则也是对可变性的封装原则。找到系统的可变因素,并将其封装起来。把一种可变性封装为一个对象,那么这种可变性的不同表象就是这个类的具体子类。
3。里氏代换原则(LSP)
里氏代换原则是继承复用的基石:在任何父类出现的地方都可以用它的子类来替代。例如正方形和长方形。正方形是一种特殊的长方形。但是正方形却不能作为长方形的子类。因对长方形的操作并不一定能套用在正方形上。如:resize().如果正方形作为长方形的子类的话,那么就会出现很多问题。不符合里氏代换原则,正方形就不能作为长方形的子类,应该把正方形和长方形都作为四边形的子类。在实际,设计类的阶层体系结构时,这是一条很重要的原则。不应该作为子类不能硬套。
4。依赖倒转原则(DIP)
依赖倒转原则是:要依赖于抽象,不要依赖于具体的实现。
为什么叫依赖倒转原则呢? 在传统的过程性系统中,高层的模块依赖于低层次的模块,抽象层次依赖于具体层次。这样导致了底层的任何改变都会影响到上层。这样的软件系统没有可维护性而言。
抽象层次应该不依赖于具体的实现细节,这样才能保证系统的可复用性和可维护性,这也就是所谓的倒转。
在实际中如何应用这一原则呢?
要针对借口编程,而不针对实现编程。那么当实现变化时,不会影响到其他的地方。 在java中应当使用接口和抽象类来进行变量的类型声明,参数的类型声明,方法的返回值类型,等等。比如: List list = new Linkedlist();
以抽象的方式进行耦合是依赖倒转原则的关键。
依照依赖倒转原则,在系统中会出现大量的类,如抽象类和接口,因为它假定所有的具类都是有可能变化的。但实际上这也不总是正确的。有一些具体类是非常稳定的,就不需要为他发明一种新的类型。
5。 接口隔离原则(ISP)
使用多个专门的借口总是比使用单一的总接口要好。一个接口应该仅代表一个角色,而不应该把所有的操作都封装到一个接口当中。准确地划分角色和其所对应的接口,不应该把没有关系的接口合并到一起。
6。合成/聚合复用原则
聚合: 表示拥有或整体与部分的关系。
合成:更强的聚合关系。整体负责部分的生命周期,整体和部分是不可分的,部分是不能被共享的。比如孙悟空 ,他的四肢, 和他的武器。悟空和四肢的关系就是合成,而和武器之间的关系就是聚合。
在面向对象中,有继承和合成/聚合两种基本服用方法。合成/聚合是将已有对象作为自己的成员,新对象调用对象已有的功能。这有很多优点:
1) 这种复用是黑箱操作,把成员对象的细节封装起来。
2)这种复用可以动态的改变,新对象可以动态的引用其他的同类对象。
继承复用有如下的优点和缺点。
1)容易实现。
2)继承复用破坏了包装,将超类的实现细节暴露给子类。
3)同时如果超类发生改变,子类也不得不跟着改变。
4)继承是静态的。
尽量使用和成/聚合而不是继承来实现复用。而区分这两种关系,区分”Has-A”和“Is-A”的关系。能正确的区分两者,应该就能正确的使用这两种复用方式。例如:人和角色。通常使用继承,可以把每个角色作为人的子类。如学生,雇员,经理等。但是继承是静态的,一旦一个人具有了一个角色后就不能再拥有其他的角色。比如一个人是经理,但它也是雇员,也可能是学生。这显然是不合理的。应该采用合成/聚合复用原则。实际上人与角色是“Has-A”而不是”Is-a”的关系,并且只有当满足里氏代换原则的时候才能使“Is-a”的关系。
在java api中 Properties 继承 Hashtable。Proterties具有了Hashtable的所有行为。实际上,Properties根本就不是一种Hashtable.更严重的是把Properties可以向上转型为Hashtable,,绕过Properties接口,调用Hashtable的方法来对Properties操作,会导致Properties的内部矛盾和崩溃。他们之间可以是“Has-a”,但不能是”Is-a”的关系。在实际中,决不能因为代码和功能上的复用而滥用继承,使用继承时一定要满足里氏代换原则。
7。迪米特法则(LoD)
迪米特法则: 一个对象应当对其他对象尽可能少的了解。以下是一些表述:
1) 只和你直接的朋友通信
2) 不要和陌生人讲话。
3)每一个软件单位都对其他单位只有最少的知识。
其目的就是降低各个单元的耦合,提高系统的可维护性。在模块之间,其通信应该只通过彼此的API来通信,而不理会模块的内部工作原理。它可以使各个模块耦合程度降到最低,促进软件的复用。
这是我的第一篇读书笔记,其中所述难免有错误和遗漏,希望大家多多指教,大家共同进步!!