Chinaunix首页 | 论坛 | 博客
  • 博客访问: 960124
  • 博文数量: 134
  • 博客积分: 7443
  • 博客等级: 少将
  • 技术积分: 1411
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-10 20:18
文章分类

全部博文(134)

文章存档

2012年(7)

2011年(29)

2010年(16)

2009年(6)

2008年(18)

2007年(58)

分类: C/C++

2007-03-03 19:47:22

软件分析和设计的阶段,开发人员学习问题领域的各种概念,分析功能,思考如何去分割并征服,想象系统的样子。在整个过程中,不断地学习,不断地发现又不断地创造。模糊的概念,错综的流程和捉摸不定的灵感无序地出现在大脑,就像飘来浮去的幻影。有时候感觉脑中满满的,乱的像一锅粥;有时候感觉脑中空空的,没有什么明确的思路,无从着手。
 
思维是十分复杂的活动,但仍然有规律。探索开发过程的思维规律,无疑是有益的。抽象是人类认识复杂世界的基本方法。同样,其方法广泛用于软件分析和设计的过程中。无论结构化程序设计,还是在面向对象程序设计。
 
面向对象的设计,其主要任务是对象模型的构造。一个较劲朋友问我什么是对象模型,我的答案是:对象和对象之间的关系组成的系统。在面向对象的技术中,抽象方法是发现对象的基本方法,十分重要。一个巧合:Abstraction 翻译成中文是“抽象”,就像是“抽取对象”的缩写。
 
本文的探讨,主要应用在面向对象的技术中。当然基本原理都是相通的:甚至,你编写SHELL脚本的时候,你也能使用面向对象的基本原理来提高你产品的质量。
什么是抽象
抽象在软件方法中有着特殊的含义?“抽象”这个概念有点抽象。
 
现实世界的东西所以能够被分类,是因为同一类的东西表现出相似性,根据你的思考角度,在一个较高的层次上,考虑你关注的共性,忽略不同个体的个性。在这个角度上思考的时候,你就可以忽略其个体差异造成的复杂。这样,你就得到了一个东西的抽象。在本文中,“抽象”这个词有两个含义:一个代表“思维方法”;一个代表“抽象出的概念”
 
抽象时除了关注东西的本质属性,还要考虑观察问题的角度。在一个人事管理系统中的人的抽象和在CS游戏中人的抽象,肯定具有不同的行为和属性。
 
虽然说是“发现”共性。有时更像是“发明”。比如:C语言的printf函数之所以能够打印所有基本类型的变量,是因为设计者创造了格式符的概念。抓住了的所有基本类型的变量有共性:可以根据指定的格式打印,但设计者必须发明“格式符”的概念来体现他。这里我使用这个例子,也是说明:抽象不光用在面向对象的领域。打印字符串、打印整数和打印小数,本来是多种行为,但作者发现(更像发明)其共性,才设计了这个更加通用的函数。而不是设计一堆函数:printString, printInt,PrintFloat等。
 
抽象、分类法和层次
这是三个密切不可分的概念。在已有的抽象上再次抽象,就出现的分类的层次。面向对象的概念中,继承体现了层次。对象之间的父子关系,体现了父类型和子类型的关系。在对象建模的过程中,我常常使用分类法来激发灵感。比如思考:我要处理的数据一共有几类,同类的数据是否可以统一处理?
 
对象来自哪里
对象可能来自问题领域也可能来自实现领域。有下面的几种情况:
1.       有的对象直接代表用户需求描述或功能描述中的概念,这类一般是真实世界的实体:如汽车,拖拉机。
2.       有的对象直接是功能分割的一个单元,习惯结构化编程的人,特喜欢设计这种对象,如文件分析器,数据处理器。这种方法很容易误导,要小心使用。构造几个互相协作,责任明确的对象完成功能。一般使用角色和责任分配的说法,不使用功能分配的方法。细想起来,模块化设计和面向对象设计的本质都是“分割并且征服”,有些情况下许多东西也是相通的。
3.       有些对象直接对应计算机上硬件或系统的资源。如内存池,日志文件,线程对象。实现这些类的时候,注意设计合理的接口,这种类一旦实现,今后可以反复使用。
4.       用来实现其他类的工具对象。如:C++的各种容器,String等,这些也是可以被反复重用的对象实现。
面向对象的应用。有三个最为成功的领域:模拟现实领域、计算机图形领域和图形界面开发库。是因为这三个领域,很容易抽象出来对象。而大多情况下,抽象对象并不是一个容易的工作。从这个角度看:一些传统的开发人员,特别是UNIX程序员反对面向对象技术,也是有其道理的。
 
避免过度的抽象
对象模型本质上也是分割并且征服。分割并且征服,是对付复杂度的基本方法。但有的时候过度分割反而导致复杂性。Linux之父Linus说:把一个复杂的东西,分割成两部分,每一个部分是简单了,但多出了要处理两个部分关系的复杂性。在对象建模的过程中,要避免过度的抽象。
 
比如:“狗和羊,成为不同类的对象也许是合理的,但花狗和黑狗没有必要使用不同的类定义,只需要给狗这个类型定义一个颜色属性就可以了”。
 
在实践中,我发现设计模式的不当使用,常常带来过度抽象。我曾经建议一个新手:减少flag功能的全局变量的使用。后来这个认真的人,把每一个flag变化都放到了一个singleton对象中封装起来,这样看起来是没有全局变量了,却增加了一堆新的类。哦,天哪!避免全局变量,是要避免使用这个全局变量的模块之间的紧耦合,现在全局变量变成singleton对象了,并且因为一个类带来的接口要比一个全局变量复杂,实际上是耦合的更紧了。又有一个同志,对工厂类模拟虚拟构造的原理研究的炉火纯青,设计中,几乎要把所有的对象都使用工厂来构造,给我的感觉:一个阔少有很多工厂来给自己造内裤,不同的分厂生产不同样式的内裤――有钱烧的。关于设计模式不当使用的情况,我今后将有一个文章详细探讨。
 
请记住著名的KISS原则:Keep It Simple, Stupid!你能找到很多理由把简单问题搞复杂,但往往能找到更好的理由把复杂问题搞简单。抽象是为了处理复杂性,不要越抽象越复杂。
 
封装驱动抽象
封装是面向对象的基本特征。我的一个经验,就是借助封装的思路来抽象对象。每当碰到复杂并且容易变化的逻辑,我就想:是不是设计一个对象把它封装起来。我把这种思考设计的方法称为:封装驱动抽象。
 
在软件系统中,可以被封装的东西很多。最常见的是一个实体的状态和行为可以封装,抽象出一个对象。整个系统的某个相对独立的特性和行为也可以被封装,如:一个程序中,许多模块都要记录日志,就可以把日志抽象成一个对象,提供日志记录的接口。某种容易变化的业务逻辑,也可以被封装。一个复杂的算法,也可以被封装。
 
被封装和抽象的东西,一般是:直接的概念、可以分离的特性、相对独立的知识或是容易变化的因素。每当看到这种东西,我都下意识的想到抽象。
 
抽象不一定成为对象
前面也说过,抽象不是面向对象技术中独有的方法。甚至在面向对象的设计中,抽象也不一定成为对象。使用对象封装就是:把知识和逻辑封装到对象中,对象的使用者就可以变得愚蠢和简单;如果设计通用的数据结构或灵活的配置文件,甚至可以把知识和逻辑封装到数据或配置中,这样你的程序就可以变得愚蠢和简单,所谓简单是可靠之母。我就喜欢傻壮的程序。我的另一个文章《被忽视的方法mini-language》专门对这种方法进行了介绍。
阅读(2956) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~