OOD沉思录 --- 面向动作与面向对象 --- 避免全能类
面向过程的软件开发通过非常集中化的控制机制来分解功能,在程序设计中表现就是大量的条件判断,深层次的循环嵌套等。
这种模式下,我们可以通过分析方法的参数,局部变量及其访问的全局变量来得到方法对数据的依赖性,但是我们只有分析整个项目代码才能得到数据对方法的依赖性。
面向对象的软件开发主要关注在非常分布的环境中分解数据以及同数据相关的功能。通过高内聚(数据与其相关行为的高度融合)和低耦合(实体之间的相互隔离)来 实现功能的分布,典型表现就是不需要过多的条件判断以及深层次的循环嵌套等。
这里我们可以通过类本身就得到数据与方法相互之间的依赖性,而不是需要分析整合程序。
当然,面向过程并非永远黑暗,面向对象也并非永远一片光明。
面向对象是面向过程发展历程上改革性的一步,其有两个典型的容易诱惑设计者的危险:
第一:容易创建庞大的“万能类”,该类包含过多的功能;
第二:容易创建“泛滥成灾的类”,整个程序被淹没在类的海洋中。
3.1 在水平方向尽可能统一地分布系统功能,也即,按照设计,顶层累应当统一的共享工作。
人们思考问题时,经常是跳跃性的,该给动物们建立一个模型了。 哦,是动物,有哺乳动物,两栖动物。。。,有海洋动物,陆地动物。 wait wait。。。,什么?海洋动物?怎么好像这种分法很别扭。
如果你在建类的时候在这里停顿下来,那恭喜你,你对了,这种分法并不是按照统一的规则进行分类,在统一水平线上功能存在很多交叉,哺乳动物可能具有海洋动物和陆地动物的行为,而海洋动物可能也具有哺乳动物和两栖动物的行为,使用者不可避免需要大量的If ...Else...来进行类型的区分。
3.2 在系统中不要创建全能类/对象。
对名字包含Driver,Manager,System,SubSystem的类要特别加以小心。
3.3 对公共接口中定义了大量的Get/Set访问方法的类要多加小心。
大量的访问方法意味着相关的数据和行为没有集中存放。我们不应该要求使用者去关心一个类里能Get到多少内容,更应该让使用者关心的是这个类能Do多少事情。
对汽车类说,如果让驾驶员Get发动机是没有意义的,我们的汽车对驾驶员来说更应该像下面:
- class 汽车
- {
- void 加速();
- void 停止();
- void 左转();
- }
而不是下面这样
- class 汽车
- {
- 发动机 Get发动机();
- 方向盘 Get方向盘();
- }
3.4 对包含太多互不沟通的行为的类多加小心。
互不沟通的行为是指只访问了类的一部份数据成员,全能类经常有互补沟通的行为。 在写一个类的时候经常会有如下形式:
- class XXXX
- {
- //序列化操作
- string filename;
- void Save();
- void Load();
- ......
- //***操作
- 。。。
- //@@@操作
- }
如果一个类的公共接口里的方法需要进行分类描述的话,基本上可以确认属于这种情况了。
3.5 有用户界面交互的程序中,界面应当依赖模型,模型不应当依赖界面。
考虑DIP原则,则界面本身影像有抽象接口。
3.6 尽可能地按照现实世界建模。
实际中我们经常为了遵守系统功能分布原则,避免全能类原则以及集中放置相关数据和行为的原则而违背这条原则。
这条原则对维护人员非常有利。