1 设计规范
这里主要摘取了OO设计原则的其中几条,其中开闭原则、里氏替换原则必须遵守,单一职
责原则尽量要求遵守,接口分离、合成聚合、依赖倒置原则强烈推荐遵守,另外补充了一些
其他规范。
1.1 开闭原则(Open-Closed Principle, OCP)
这是最基本的原则,一个模块应当对扩展开放,对修改关闭。确切地说,模块的对外接口
不能被修改,而只能被扩展,当某个开发者使用了你的接口,而却被你后期修改了这个接口,
那对程序是一个灾难。
因此在进行设计时要尽量考虑接口封装机制、抽象机制和多态技术。
1.2 单一职责原则SRP (Simple responsibility pinciple)
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
很多人可能觉得它很简单,但在实际中,我们多有不遵守这条原则。即便是经验丰富的
程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职
责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。
比如:类T只负责一个职责P,这样设计是符合单一职责原则的。后来由于某种原因,
也许是需求变更,也许是程序的设计者境界提高,需要将职责P细分为粒度更细的职责P1,
P2,这时如果要使程序遵循单一职责原则,需要将类T也分解为两个类T1和T2,分别负责
P1、P2两个职责。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修
改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这
样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为
P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进
行重构。)
遵循单一职责的优点有:
•
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单
的多;
•
提高类的可读性,提高系统的可维护性;
•
变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功
能时,可以显著降低对其他功能的影响。
1.3 接口分离原则(the Interface Segregation Principle ISP)
一个类对另外一个类的依赖是建立在最小的接口上。使用多个专门的接口比使用单一的
总接口要好。如果说某个模块能提供接口能同时支持放大、缩小、平移操作,建议提供成三
个接口,因为存在人们只需要某一个,或者某两个接口的情况,另外当其中某个操作出错时,
也不会影响到调用另两个接口的人。
1.4 合成/聚合复用原则(Composite/Aggregate Reuse
Principle,CARP)
在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新的对象通过这
些向对象的委派达到复用已有功能的目的。这句话怎么可以理解为:要尽量使用合成/聚合,尽
量不要使用继承。
举个例子,有一个对象壶,另一个对象浇水壶,也需要用到壶的内容,但是它额外有浇
水的功能,使用壶和一个浇水的接口的组合来定义浇水壶更好于直接继承壶,因为水管也有
浇水的功能。
1.5 里氏代换原则LSP(Liskov Substitution Principle)
如果每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P
在所有的对象o1都代换称o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能
察觉出基类对象和子类对象的区别。只有衍生类可以替换基类,软件单位的功能才能不受影
响,基类才能真正被复用,而衍生类也能够在基类的基础上增加新功能。反过来的代换则不
成立。
最著名的例程为:正方形是否是长方形的子类(答案是"否")。类似的还有椭圆和圆的关系。
应当尽量从抽象类继承,而不从具体类继承,一般而言,如果有两个具体类A,B有继承关系,
那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类.即如
果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都
应当是具体类;而所有的树枝节点都应当是抽象类或者接口。
1.6 依赖倒置原则(Dependence Inversion Principle)
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应当依赖于细节,细节
应当依赖于抽象。
问题描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码
来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层
模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接
与类B或者类C发生联系,则会大大降低修改类A的几率。
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以
抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。
依赖倒置原则的核心是接口编程。在实际编程中,我们一般需要做到如下3点:
1) 低层模块尽量都要有抽象类或接口,或者两者都有。
2) 变量的声明类型尽量是抽象类或接口。
3) 使用继承时遵循里氏替换原则。
1.7 共同封闭原则 Common Closure Principle(CCP)
一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中
所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必
须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍
布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。
如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该
属于同一个包。
CCP延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改
的范围限制在一个最小范围内的包里。
1.8 共同重用原则Common Reuse Principle (CRP)
包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有
被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。
依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变,并发布新的版本,使用这
个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任
何改变。因为如果包中包含有未被使用的类,即使用户不关心该类是否改变,但用户还是不
得不升级该包并对原来的功能加以重新测试。
CCP则让系统的维护者受益。CCP让包尽可能大(CCP原则加入功能相关的类),CRP则
让包尽可能小(CRP原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。
1.9 其他
1、 遵循高内聚,松耦合的基本原则。
2、 严格遵循MVC的设计模式,界面实现与业务逻辑相分离,例:数据产品分发模块由两个
工程DataOutput,DataOutputUI组成。
3、 在模块设计时,如果模块之间有交互,那么为了方便多人开发,模块设计人需要先定义
好与其他模块之间的接口,该接口须优先实现。
4、 进行严格的异常管理,对系统操作记录日志。
5、 所有有运行环境相关的模块都必须实现自适应模块接口。运行环境:数据表,本地文件
等。
6、 对于功能独立的模块,需要支持参数传入及独立运行两种模式。