软件设计,一直是一个非常抽象的,非常难懂的领域。
然而设计,并不是科学,虽然有评价标准,却没有是非对错。好的软件,就像是艺术品,闪耀着前人智慧的结晶。
本文将探讨一下流行的软件设计,以及总结一下好的设计原则和方法。
设计的基本原则
一谈到软件设计,很多人都会讲,你要怎么怎么设计软件,怎么怎么才能面向对象,怎么样才能符合设计模式。没错,目前学软件设计,最初接触的可能就是面向对象的设计方法,23种设计模式。
但我要说,软件设计,不是死板的,不是只有这些模式的。软件设计,是更为灵活的艺术品的创作,而面向对象,可能只是其中的一个派系而已。
我们所熟悉的面向对象,只是万千设计方法中的一种,23种设计模式,也只是设计中的冰山一角,我们要跳出面向对象的小圈子,去看看外面的风景。
但无论什么样的设计方式和思想,总得有一个评价标准,或者说是一个共识,来解释什么样的设计是一个好的设计,这个基本标准就是:高内聚,低耦合。
所谓高内聚,是指一个功能,或一个模块,内部的封装性较好,同样的功能,尽量只存有一份代码,这样你在修改一个功能时,往往改动就很小。而且内聚性高的代码,很注重接口的稳定,在不修改接口的情况,修改内部的实现,对外部影响极小或根本无影响。
低耦合,是在说代码模块间的耦合性要低,模块能较容易的移动和替换,模块间的耦合性低,最大的好处就是可维护性强,一个模块出现问题,方便替换。而且复用性强,同一个模块,可以被多个地方引用,降低了软件的成本。
举个例子
我们现在要做一个在线购物的网站,有用户和管理员两种角色,需要维护一个商品列表,用户的购物车,有什么样的设计方式呢?
这里我们只是给出简单的例子,实际情况往往比这复杂的多。
方法1:
我们给每个页面写一段代码,假设我们用JavaEE实现,那么我们可能建立了几个页面,我们可以按照页面划分功能,然后依次实现,要完成的页面有:
- 登录、注册页面
- 查看商品列表页面
- 购物车页面
- 后台登录页面
- 后台管理商品列表页面
我们会用jsp写一个个页面内容,然后通过数据库操作,将对应表中的数据取出来,并格式化到前端页面上,在接受用户提交的表单时,我们为其查询数据库,并进行数据检查与过滤,然后操作对应的数据表。
经过我们苦逼程序员的一致努力,终于实现了全部的功能,网站可以使用了。
但是…客户忽然发现,他们希望再增加一种vip用户,让vip用户可以存储更多的个人信息,绑定自己的银行卡账户,这样他们能为vip用户提供更好的服务。
然而,这并不简单,由于所有的代码和页面逻辑都混在一起,虽然也有按照页面划分功能,但添加一种新的用户角色,大部分的页面都需要改动,工作量很大。
方法2:
我们先思考一下,是什么造成了改动的困难。如过单独的考虑一个用户的模型,也许就能更加简便的实现,也就是说:我们按照页面划分功能模块,这种思想太粗糙。
我们设计一个用户模型,将单独的数据操作抽象出来,方便我们扩展用户类型。通过继承的方式,我们不但能实现vip用户,甚至也可以再扩展企业用户等等。
我们这里使用了面向对象的设计方式,由于Java的对象支持非常好,我们这样设计也是能很方便的用Java类来实现,这样我们不但可以方便的将模型扩展,更可以简单的复用模型中的操作。
同理,我们继续抽象整个项目的结构,像购物车,商品,都可以抽象成单独的模型类,而关键的流程控制,更是可以抽象成控制器。
这种流行的MVC架构到现在,都在软件架构中发挥着关键的作用。
这里并不想讲解MVC架构,而是要说明一个问题,软件结构直接影响软件的可维护性,可扩展性。
高内聚、低耦合,这些都是良好软件设计中的基本观点,但更基本的设计考虑,则是软件是要分模块实现的,当然这个模块是一种对软件结构的抽象,可以说是类,也可以说是函数,总之是软件的组成部分。如果一个软件只有一个函数,所有功能从上到下依次完成,也就没有了所谓的软件设计。
软件设计的核心——抽象
软件设计的核心,我认为是对事物的抽象。
软件设计时,抽象是最基本的方法,来设计出高内聚、低耦合的代码。希望代码设计的更好,那么我们就需要一个好的抽象,一个对现实事物构建的合理的模型。
原因也很简单,软件的是为现实世界服务的,只有良好的描述现实事物特征的模型,才能更好符合我们要让其完成的功能。
前面例子讲解了,什么是好的抽象,例如将页面中的实体抽象成数据模型,将实体相关的操作抽象成模型的方法,这直接套用了面向对象的设计模式,形成了很好的抽象。
当然,还有很多种抽象方法,例如在大名鼎鼎的LISP语言中,函数就是第一等公民,将各类方法抽象成函数就是他的做法,甚至是数据的定义,也可以抽象成模型构造函数。这样就又换了个角度,从另外一个方面来审视整个项目,得出的软件结构,也大不相同。
在LISP语言中,抽象方式变了,不再那么关注数据模型,而是关注每一个功能的输入输出,将不同的功能组合在一起,形成新的功能,就是函数式编程的基本思想。
好的抽象,往往和软件是无关的,是在人脑中,将事物的特征归类,形成的有效的概念。而一种好的抽象,往往会影响一个时代的设计风格。在Unix系统中,有一个非常棒的设计,将一切抽象为文件,设备驱动也是文件,IO操作也是文件,系统的监控,也是靠文件。这种设计影响了整个业界,人们惊奇的发现,原来操作系统将一切抽象为文件后,所有的操作都变得统一和方便了。我们添加新的硬件驱动,不需要修改系统接口,用设备文件的方式就可以访问,包括进程间通信,也用的是内存共享文件,监控系统参数,只需要打开文件就可以。
编程范式对人思考的影响
我一直说,千万不要局限在设计模式中。原因很简单,固定的设计模式往往会阻碍你对更好的设计的追求。有时会有种一叶障目的感觉。
判断一款软件是否设计的较好,最简单的方式是这样,先看有没有冗余代码,消除冗余是代码优化的第一步。
一般情况,总会有办法能将冗余代码合并在一起。但是不是没有冗余就是好的设计了呢?这可不一定。因为很有可能你虽然消除了冗余代码,但引入了复杂的结构,这样是得不偿失的。还有,就是要看可读性,如果消除冗余代码时,降低了代码的可读性,本来功能很清楚的一个函数,经过冗余消除后,反而看不懂是在做什么了,这时,就要避免代码整合。
或者说,这时的另外一种方式就是,构建新的抽象方式,抽象新的概念,这样就能让思考转换,构建出既没有冗余,又容易理解的代码。
我很喜欢多范式编程语言,这类语言不规定你具体要使用什么样的编程模型,你*发挥的空间就广,代表就是C++、LISP、Scala等,都是十分优秀的语言。
如果你只属性面向对象的设计,那么我强烈建议你去看一看函数式设计和面向方面的AOP编程,往往会对你的设计具有启发式的意义。
分层是解决软件设计问题的有力方法
其实分层的思想和抽象是一脉相承的,一批软件功能的整体抽象,往往就能形成一个中间层,绝大部分复杂问题,都可以通过分层的思想,逐层分而治之,将任务不断简化,最后问题得解。
例如我们的互联网,互联网就是分层解决问题的典范,在TCP/IP协议下,有对等层的概念。我们知道,TCP/IP协议族采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
协议分为:网络接口层、网络层、传输层、应用层。网络接口层就解决两个硬件设备直连的通信的问题,通过出错重传机制,保障直连信息的可靠传输;网络层则负责寻找目标地址,分组转发和路由,将数据填上ip报文段,发送到目标机;传输层则复杂格式化信息流,并提供稳定可靠传输,为此,传输必须通过三次握手才能开始,而且每个报文包更是要经过接收方发回确认才算安全传输。
如此复杂的互联网,通过分层的解决方案,得到了可靠有效的实现。现如今互联网的蓬勃发展,也证明了当年的设计确实是灵活合理的。
软件设计领域,有这样一句著名的命题:
任何一个软件设计的难题,都可以通过增加一个中间层来实现。
我们这里不去论证这个命题是否合理,但分层确实是人类这么多年设计经验的精华。分层体现了两个重要的思想,分治和抽象,这是解决问题的关键。
本文属 西风逍遥游 原创, 转载请注明出处: 西风世界 http://blog.csdn.net/xfxyy_sxfancy