软件项目中,需求是不断变化的,需求也是项目中最难把控的,需求的变更也是无法避免的。我们写的软件程序,如何能实现拥抱变化,使我们的软件达到可维护和可复用,这是一代代软件工程师不断追寻的真理。
1.软件的可维护性和可复用性
导致一个软件的可维护性较低的原因有四个:
1.过于僵硬(Rigidity):比如在系统中新增一个功能,会变得非常复杂,涉及到很多模块的调整,这就是系统僵硬的体现。 2.过于脆弱(Fragility):比如对程序中某一个地方的修改,导致看上去没有什么关系的其他地方产生了影响,修改的同时,没有人能预测改动会给系统带来什么风险。 3.复用率低(Immobility):比如想使用程序中已有的一段代码、函数、模块时,这些已有的代码总是依赖一大堆其它的东西,很难将它们独立出来使用。 4.黏度过高(Viscosity):如果一个系统设计,不能简单的复用一个类或者通过接口来实现扩展,想扩展一个系统功能,必须破坏原始架构,就是黏度过高。
一个好的系统设计应该有如下的性质:
1.可扩展性(Extensibility):可以很容易的在系统中加入一个新的功能。 2.灵活性(Flexibility):可以很容易的实现对某个代码的修改,而不担心对其他模块产生影响。 3.可插入性(Pluggability):可以很容易的将一个类抽出去复用,或者将另一个有同样功能的接口的类加入到系统里。
2.面向对象设计的七大原则

开闭原则(Open Close Principle)
开放封闭原则就是对扩展开放、对修改封闭,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。软件需求总是变化的,世界上没有一个软件的是不变的,因此对软件设计人员来说,必须在不需要对原有系统进行修改的情况下,实现灵活的系统扩展。
单一职责原则(Single Responsibility Principle)
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;提高类的可读性,提高系统的可维护性;变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
依赖倒置原则(Dependence Inversion Principle)
具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类;而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口;这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。
里氏替换原则(Liskov Substitution Principle)
里氏替换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
接口隔离原则(Interface Segregation Principle)
提供尽可能小的单独接口,而不要提供大的总接口。暴露行为让后面的实现类知道的越少越好。譬如类ProgramMonkey通过接口CodeInterface依赖类CodeC,类ProgramMaster通过接口CodeInterface依赖类CodeAndroid,如果接口CodeInterface对于类ProgramMonkey和类CodeC来说不是最小接口,则类CodeC和类CodeAndroid必须去实现他们不需要的方法。将臃肿的接口CodeInterface拆分为独立的几个接口,类ProgramMonkey和类ProgramMaster分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
其实整个设计模式就是在讲如何类与类之间的组合/聚合。在一个新的对象里面通过关联关系(包括组合关系和聚合关系)使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。也就是,要尽量使用类的合成复用,尽量不要使用继承。
如果为了复用,便使用继承的方式将两个不相干的类联系在一起,违反里氏代换原则,哪是生搬硬套,忽略了继承了缺点。继承复用破坏数据封装性,将基类的实现细节全部暴露给了派生类,基类的内部细节常常对派生类是透明的,白箱复用;虽然简单,但不安全,不能在程序的运行过程中随便改变;基类的实现发生了改变,派生类的实现也不得不改变;从基类继承而来的派生类是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。
迪米特法则(Law Of Demeter)
类与类之间的关系越密切,耦合度也就越来越大,只有尽量降低类与类之间的耦合才符合设计模式;对于被依赖的类来说,无论逻辑多复杂都要尽量封装在类的内部;每个对象都会与其他对象有耦合关系,我们称出现成员变量、方法参数、方法返回值中的类为直接的耦合依赖,而出现在局部变量中的类则不是直接耦合依赖,也就是说,不是直接耦合依赖的类最好不要作为局部变量的形式出现在类的内部。