第五章系统设计 系统设计是把需求转化为软件系统的最重要的环节。系统设计的优劣在根本上决定 了软件系统的质量。就象“一切帝国主义都是纸老虎”那样可以断定“差的系统设计必 定产生差的软件系统。”所以我们要努力保证系统设计“根正苗红”,把一切左倾、右倾 的设计思潮消灭在萌芽状态。 Windows NT的一位系统设计师拥有8辆法拉利跑车,让Microsoft公司的一些程序 员十分眼红。但你只能羡慕而不能愤恨,因为并不是每个程序员都有本事成为复杂软件 系统的设计师。系统设计要比纯粹的编程困难得多。即便你清楚客户的需求,却未必知 道应该设计什么样的软件系统一一既能挣最多的钱又能让客户满意。“天下西湖三十六, 最美是杭州”,千年前苏东坡大学士对西湖精采绝伦的系统设计,使杭州荣升为“天堂”, 让后人只剩下赞叹和破坏的份了。 本章讲述系统设计的四方面内容:体系结构设计、模块设计、数据结构与算法设计 用户界面设计。如果将软件系统比喻为人体,那么: (1)体系结构就如同人的骨架。如果某个家伙的骨架是猴子,那么无论怎样喂养和美容, 这家伙始终都是猴子,不会成为人。 (2)模块就如同人的器官,具有特定的功能。人体中最出色的模块设计之一是手,手只 有几种动作,却能做无限多的事情。人体中最糟糕的模块设计之一是嘴巴,嘴巴将最有 价值但毫无相干的几种功能如吃饭、说话、亲吻混为一体,使之无法并行处理,真乃人 类之不幸。 (3)数据结构与算法就如同人的血脉和神经,它让器官具有生命并能发挥功能。数据结 构与算法分布在体系结构和模块中,它将协调系统的各个功能。人的耳朵和嘴巴虽然是 相对独立的器官,但如果耳朵失聪了,嘴巴就只能发出“啊”“鸣”的声音,等于丧失了 说话的功能(所以聋子天生就是哑巴),可人们却又能用手势代替说话。人体的数据结构 与算法设计真是十分神奇并且十分可笑。 (4)用户界面就如同人的外表,最容易让人一见钟情或一见恶心。象人类追求心灵美和 外表美那样,软件系统也追求(内在的)功能强大和(外表的)界面友好。但随着生活 节奏的加快,人们已少有兴趣去品味深藏不露的内在美。如果把U系统比作是健壮的 汉子和妇人,那么Windows系统就象妩媚的小白脸和狐狸精。想不到Windows系统竟 然能兴风作浪,占去大半市场。有鉴于此,我们应该鼓励女士多买化妆品(男士付钱) 以获得更好的界面。 在进行系统设计时,我们要深情地关注软件的质量因素,如正确性与精确性、性能 与效率、易用性、可理解性与简法性、可复用性与可扩充性等等。即使把系统设计做好 了,也并不意味着就能产生好的软件系统。在程序设计、测试、维护等环节还要做大量 的工作,无论哪个环节出了差错,都会把好事搞砸了。据说上帝把所有的女士都设计成 天使,可是天使们在下凡时有些双脚先着地,有些脸先着地。上帝的这一疏忽让很多女 孩伤透了心。我们在开发软件时,一定要吸取这个教训
1 第五章 系 统 设 计 系统设计是把需求转化为软件系统的最重要的环节。系统设计的优劣在根本上决定 了软件系统的质量。就象“一切帝国主义都是纸老虎”那样可以断定“差的系统设计必 定产生差的软件系统。”所以我们要努力保证系统设计“根正苗红”,把一切左倾、右倾 的设计思潮消灭在萌芽状态。 Windows NT 的一位系统设计师拥有 8 辆法拉利跑车,让 Microsoft 公司的一些程序 员十分眼红。但你只能羡慕而不能愤恨,因为并不是每个程序员都有本事成为复杂软件 系统的设计师。系统设计要比纯粹的编程困难得多。即便你清楚客户的需求,却未必知 道应该设计什么样的软件系统——既能挣最多的钱又能让客户满意。“天下西湖三十六, 最美是杭州”,千年前苏东坡大学士对西湖精采绝伦的系统设计,使杭州荣升为“天堂”, 让后人只剩下赞叹和破坏的份了。 本章讲述系统设计的四方面内容:体系结构设计、模块设计、数据结构与算法设计、 用户界面设计。如果将软件系统比喻为人体,那么: (1)体系结构就如同人的骨架。如果某个家伙的骨架是猴子,那么无论怎样喂养和美容, 这家伙始终都是猴子,不会成为人。 (2)模块就如同人的器官,具有特定的功能。人体中最出色的模块设计之一是手,手只 有几种动作,却能做无限多的事情。人体中最糟糕的模块设计之一是嘴巴,嘴巴将最有 价值但毫无相干的几种功能如吃饭、说话、亲吻混为一体,使之无法并行处理,真乃人 类之不幸。 (3)数据结构与算法就如同人的血脉和神经,它让器官具有生命并能发挥功能。数据结 构与算法分布在体系结构和模块中,它将协调系统的各个功能。人的耳朵和嘴巴虽然是 相对独立的器官,但如果耳朵失聪了,嘴巴就只能发出“啊”“呜”的声音,等于丧失了 说话的功能(所以聋子天生就是哑巴),可人们却又能用手势代替说话。人体的数据结构 与算法设计真是十分神奇并且十分可笑。 (4)用户界面就如同人的外表,最容易让人一见钟情或一见恶心。象人类追求心灵美和 外表美那样,软件系统也追求(内在的)功能强大和(外表的)界面友好。但随着生活 节奏的加快,人们已少有兴趣去品味深藏不露的内在美。如果把 Unix 系统比作是健壮的 汉子和妇人,那么 Windows 系统就象妩媚的小白脸和狐狸精。想不到 Windows 系统竟 然能兴风作浪,占去大半市场。有鉴于此,我们应该鼓励女士多买化妆品(男士付钱) 以获得更好的界面。 在进行系统设计时,我们要深情地关注软件的质量因素,如正确性与精确性、性能 与效率、易用性、可理解性与简法性、可复用性与可扩充性等等。即使把系统设计做好 了,也并不意味着就能产生好的软件系统。在程序设计、测试、维护等环节还要做大量 的工作,无论哪个环节出了差错,都会把好事搞砸了。据说上帝把所有的女士都设计成 天使,可是天使们在下凡时有些双脚先着地,有些脸先着地。上帝的这一疏忽让很多女 孩伤透了心。我们在开发软件时,一定要吸取这个教训
5.1体系结构设计 杨叔子院子曾这样指点其弟子: 文学中有科学,音乐中有数学,漫画中有现代数学的拓扑学。漫画家可以“几笔 就把一个人画出来,不管怎么美化或丑化,就是活像。为什么?因为那“几笔”不是别 的,而是拓扑学中的特征不变量,这是事物最本质的东西! 体系结构是软件系统中最本质的东西: (1)体系结构是对复杂事物的一种抽象。良好的体系结构是普遍适用的,它可以高效地 处理多种多样的个体需求。一提起“房子”,我们的脑中马上就会出现房子的印象(而不 是地洞的印象)。“房子”是人们对住宿或办公环境的一种抽象。不论是办公楼还是民房, 同一类建筑物(甚至不同类的建筑物)之间都具有非常相似的体系结构和构造方式。如 果13亿中国人民每个人都要用特别的方式构造奇异的房子,那么960万平方公里的土地 将会变得千疮百孔,终日不得安宁。 (2)体系结构在一定的时间内保持稳定。只有在稳定的环境下,人们才能干点事情,社 会才能发展。科学告诉我们,字宙间万物无时无刻不在运动、飞行。由于我们的生活环 境在地球上保持相对稳定,以致于我们可以无忧无虑地吃饭和睡觉,压根就意识不到自 己是活生生的导弹。软件开发最怕的就是需求变化,但“需求会发生变化”是个无法逃 避的现实。人们希望在需求发生变化时,最好只对软件做些皮皮毛毛的修改,可千万别 改动软件的体系结构。就如人们对住宿的需求也会变动,你可以经常改变房间的装璜和 良好的体系结构意味若普适、高效和稳定。本节将论述两种非常通用的软件体系结 构:层次结构和客户机V服务器(Client//Server)结构 5.11层次结构 层次结构表达了这么一种常识:有些事情比较复杂,我们没法一口气干完,就把事 情分为好几层,一层一层地去做。高层的工作总是建立在低层的工作之上。层次关系主 要有两种:上下级关系和顺序相邻关系。 一、上下级关系的层次结构 我们从小学一直读到博士研究生毕业,要读20多年,可以分为五个层次。而范进的 知识结构只有两层:“私塾”和“秀才”,但读了五十多年,如图5.1所示。 般地处于 较高层次的学生应该懂得所有低层次的知识,而处于低层次学生无法懂得所有高层次的 知识。图5.1的层次结构存在上下级关系,如同在军队中,上级可以命令下级,而下级 不能命令上级。如果把图5.1的层次结构当成是一个软件系统的结构,那么上层子系统 可以使用下层子系统的功能,而下层子系统不能够使用上层子系统的功能
2 5.1 体系结构设计 杨叔子院子曾这样指点其弟子: 文学中有科学,音乐中有数学,漫画中有现代数学的拓扑学。漫画家可以“几笔” 就把一个人画出来,不管怎么美化或丑化,就是活像。为什么?因为那“几笔”不是别 的,而是拓扑学中的特征不变量,这是事物最本质的东西。 体系结构是软件系统中最本质的东西: (1)体系结构是对复杂事物的一种抽象。良好的体系结构是普遍适用的,它可以高效地 处理多种多样的个体需求。一提起“房子”,我们的脑中马上就会出现房子的印象(而不 是地洞的印象)。“房子”是人们对住宿或办公环境的一种抽象。不论是办公楼还是民房, 同一类建筑物(甚至不同类的建筑物)之间都具有非常相似的体系结构和构造方式。如 果 13 亿中国人民每个人都要用特别的方式构造奇异的房子,那么 960 万平方公里的土地 将会变得千疮百孔,终日不得安宁。 (2)体系结构在一定的时间内保持稳定。只有在稳定的环境下,人们才能干点事情,社 会才能发展。科学告诉我们,宇宙间万物无时无刻不在运动、飞行。由于我们的生活环 境在地球上保持相对稳定,以致于我们可以无忧无虑地吃饭和睡觉,压根就意识不到自 己是活生生的导弹。软件开发最怕的就是需求变化,但“需求会发生变化”是个无法逃 避的现实。人们希望在需求发生变化时,最好只对软件做些皮皮毛毛的修改,可千万别 改动软件的体系结构。就如人们对住宿的需求也会变动,你可以经常改变房间的装璜和 摆设,但不会在每次变动时都要去折墙、拆柱、挖地基。如果当需求发生变化时,程序 员不得不去修改软件的体系结构,那么这个软件的系统设计是失败的。 良好的体系结构意味着普适、高效和稳定。本节将论述两种非常通用的软件体系结 构:层次结构和客户机/服务器(Client/Server)结构。 5.1.1 层次结构 层次结构表达了这么一种常识:有些事情比较复杂,我们没法一口气干完,就把事 情分为好几层,一层一层地去做。高层的工作总是建立在低层的工作之上。层次关系主 要有两种:上下级关系和顺序相邻关系。 一、上下级关系的层次结构 我们从小学一直读到博士研究生毕业,要读 20 多年,可以分为五个层次。而范进的 知识结构只有两层:“私塾”和“秀才”,但读了五十多年,如图 5.1 所示。一般地处于 较高层次的学生应该懂得所有低层次的知识,而处于低层次学生无法懂得所有高层次的 知识。图 5.1 的层次结构存在上下级关系,如同在军队中,上级可以命令下级,而下级 不能命令上级。如果把图 5.1 的层次结构当成是一个软件系统的结构,那么上层子系统 可以使用下层子系统的功能,而下层子系统不能够使用上层子系统的功能
二、顺序相邻关系的层次结构 顺序相邻关系的层次结构表明通讯只能在相邻两层之间发生,信息只能被一层一层 地顺序传递。这种层次结构的经典之作是计算机网络的OS1参考模型,如图5.2所示。 为了减少设计的复杂性,大多数网络都按层(Layer)或级(Level)的方式组织。每 层的目的都是向它的上一层提供一定的服务,而把如何实现这一服务的细节对上一层加 以屏蔽。一台机器上的第n层与另一台机器上的第n层进行对话。通话的规则就是第n 层的协议。数据不是从一台机器的第n层直接传送到另一台机器的第n层。发送方把数 据和控制信息逐层向下传递。最低层是物理介质,它进行实际的通讯。接收方则将数据 和培制信息溪层向上传递。 每一对相邻层之间都有接口。接口定义了下层提供的原语操作和服务。当网络设计 者在决定一个网络应包含多少层,每一层应当做什么的时候,其中很重要的工作是在相 邻层之间定义清晰的接口。接口可以使得同一层能轻易地用某一种实现(Implementation) 来替换另一种完全不同的实现(如用卫星信道来代替所有的电话线),只要新的实现能向 上层提供同一组服务就可以了。【Tanenbaum1998 博士(34年) 硕士(23年) 考上“举人”时已五十多岁了 举人 本科(4年) 复习报考“举人”用了几十年 中学(6年) 秀才 私 小学(5-6年) 图5.1(a)从小学读到博士存在的五个学习阶段 图5.1(b)范进的知识结构 应用层 拉用层
3 二、顺序相邻关系的层次结构 顺序相邻关系的层次结构表明通讯只能在相邻两层之间发生,信息只能被一层一层 地顺序传递。这种层次结构的经典之作是计算机网络的 OSI 参考模型,如图 5.2 所示。 为了减少设计的复杂性,大多数网络都按层(Layer)或级(Level)的方式组织。每一 层的目的都是向它的上一层提供一定的服务,而把如何实现这一服务的细节对上一层加 以屏蔽。一台机器上的第 n 层与另一台机器上的第 n 层进行对话。通话的规则就是第 n 层的协议。数据不是从一台机器的第 n 层直接传送到另一台机器的第 n 层。发送方把数 据和控制信息逐层向下传递。最低层是物理介质,它进行实际的通讯。接收方则将数据 和控制信息逐层向上传递。 每一对相邻层之间都有接口。接口定义了下层提供的原语操作和服务。当网络设计 者在决定一个网络应包含多少层,每一层应当做什么的时候,其中很重要的工作是在相 邻层之间定义清晰的接口。接口可以使得同一层能轻易地用某一种实现(Implementation) 来替换另一种完全不同的实现(如用卫星信道来代替所有的电话线),只要新的实现能向 上层提供同一组服务就可以了。[Tanenbaum 1998] 考上“举人”时已五十多岁了 复习报考“举人”用了几十年 图 5.1(a)从小学读到博士存在的五个学习阶段 图 5.1(b)范进的知识结构 数据链路层 网络层 传输层 会话层 表示层 应用层 主 机 A 主 机 B 小 学 (5-6 年) 中 学(6 年) 本 科(4 年) 硕 士(2-3 年) 博 士(3-4 年) 数据链路层 网络层 传输层 会话层 表示层 应用层 私 塾 秀 才 举人
物理层 物理层 图52计算机网络的OS1参考模型 三、其它的层次结构 目前在大型商业应用软件系统中还流行一种包含中间件(Middleware)的层次结构, 如图5.3所示Jacobson1997]。中间件支持与平台无关的分布式计算,可以用DCOM和 CORBA对象来实现 Application Systems Business-specific Components Middleware System Software 图5.3包含中间件的层次结构 5.1.2客户机/服务器结构 让我们先回顾一下早期的电话系统。贝尔(Alexander Graham Bell)于l876年申请 了电话专利。那时期的电话必须一对一对地卖,用户自己在两个电话之间拉一根线。如 果一个电话用户想和其它几个电话用户通话,他必须拉根单独的线到每个人的房子里。 于是在很短的时间内,城市里到处都是穿过房屋和树木的混乱的电话线。很明显,企图 把所有的电话完全互联(如图5.4(a)所示)是行不通的。 贝尔电话公司在1878年开办了第一个交换局。公司为每个客户架设一条线。打电话 时,客户摇动电话的曲柄使电话公司办公室的铃响起来,操作员听到铃声以后根据要求 将呼叫方和被呼叫方用跳线手工连接起来。这种集中交换式的模型如图54(b)所示。 很快地,贝尔系统的交换局就出现在各地。人们又要求能打城市间的长途电话,就出现 了二级交换局,以后进一步发展为多个二级交换局。【Tancnbaum1998)
4 图 5.2 计算机网络的 OSI 参考模型 三、其它的层次结构 目前在大型商业应用软件系统中还流行一种包含中间件(Middleware)的层次结构, 如图 5.3 所示[Jacobson 1997]。中间件支持与平台无关的分布式计算,可以用 DCOM 和 CORBA 对象来实现。 图 5.3 包含中间件的层次结构 5.1.2 客户机/服务器结构 让我们先回顾一下早期的电话系统。贝尔(Alexander Graham Bell)于 1876 年申请 了电话专利。那时期的电话必须一对一对地卖,用户自己在两个电话之间拉一根线。如 果一个电话用户想和其它几个电话用户通话,他必须拉 n 根单独的线到每个人的房子里。 于是在很短的时间内,城市里到处都是穿过房屋和树木的混乱的电话线。很明显,企图 把所有的电话完全互联(如图 5.4(a)所示)是行不通的。 贝尔电话公司在 1878 年开办了第一个交换局。公司为每个客户架设一条线。打电话 时,客户摇动电话的曲柄使电话公司办公室的铃响起来,操作员听到铃声以后根据要求 将呼叫方和被呼叫方用跳线手工连接起来。这种集中交换式的模型如图 5.4(b)所示。 很快地,贝尔系统的交换局就出现在各地。人们又要求能打城市间的长途电话,就出现 了二级交换局,以后进一步发展为多个二级交换局。[Tanenbaum 1998] 物理层 System Software Middleware Business-specific Components Application Systems 交换局 物理层
5.4(a)完全互联的电话系统 5.4(b)集中交换式的电话系统 如果将图5.4(b)中的电话看成是客户程序,将中心的交换局看成是服务程序,那 么图5.4(b)就是典型的客户机/假务器结构。注意这里客户机和服务器都是指软件而不 是指硬件(一台计算机可以放多个客户机和服务器软件)。 客户机V服务器结构存在两个显然的优点: (1)以集中的方式高效率地管理通讯。前面讲电话系统的故事就是要说明这一点。 (2)可以共享资源。比如在信息管理系统中,服务器将信息集中起来,任何客户机都可 以通过访问服务器而获得所需的信息。 客户机和服务器之间的通讯以“请求一一响应”的方式进行。客户机先向服务器发 起“请求”(Request),服务器再响应(Response)这个请求,如图5.5所示。 请求 客户机 服务器 响应 图5.5 Client和Server之间的通讯以“请求一一响应”的方式进行 采用“请求一一响应”这种通讯方式的基本动机是为了解决“聚集”(Rendezvous) 问题。为了理解这一个问题,设想一个人试图在分离的机器上启动两个程序并让它们进 行通讯。还需记住,计算机的运行速度要比人的操作速度高出许多数量级。在他启动第 一个程序后,该程序开始执行并向对等程序发送消息。在几个微秒内,它便发现对等程 序还不存在,于是就发出一条错误消息,然后退出。此后,他启动了第二个程序。不幸 的是,当第二个程序开始执行时,它也找不到第一个程序(早已退出)。即使这两个程序 连续地重新试者通讯,但由于它们的执行速度那么高,以致于它们在同一瞬间联系上的 概率非常低。在客户机/服务器结构中,服务器在启动后必须(无限期地)等待客户机的 “请求”,因此就形成了“请求一 一响应”的通讯方式。 在Internet/Intranet领域,目前“浏览器一Web服务器一数据库服务器”结构是 种非常流行的客户机U服务器结构,如图5.6所示。这种结构最大的优点是:客户机统 采用浏览器,这不仅让用户使用方便,而且使得客户机端不存在维护的问题。当然,软 件开发布和维护的工作不是自动消失了,而是转移到了Wb服务器端。在Web服务器 端,程序员要用脚本语言编写响应页面。例如用Microsoft的ASP语言查询数据库服务 器,将结果保存在Wb页面中,再由浏览器显示出来。 客户机 eb服务器 HTP请求 数据库 查询 浏览器 ASP Engine 服务器 HTP响应
5 5.4(a)完全互联的电话系统 5.4(b)集中交换式的电话系统 如果将图 5.4(b)中的电话看成是客户程序,将中心的交换局看成是服务程序,那 么图 5.4(b)就是典型的客户机/服务器结构。注意这里客户机和服务器都是指软件而不 是指硬件(一台计算机可以放多个客户机和服务器软件)。 客户机/服务器结构存在两个显然的优点: (1)以集中的方式高效率地管理通讯。前面讲电话系统的故事就是要说明这一点。 (2)可以共享资源。比如在信息管理系统中,服务器将信息集中起来,任何客户机都可 以通过访问服务器而获得所需的信息。 客户机和服务器之间的通讯以“请求——响应”的方式进行。客户机先向服务器发 起“请求”(Request),服务器再响应(Response)这个请求,如图 5.5 所示。 请求 响应 图 5.5 Client 和 Server 之间的通讯以“请求——响应”的方式进行 采用“请求——响应”这种通讯方式的基本动机是为了解决“聚集”(Rendezvous) 问题。为了理解这一个问题,设想一个人试图在分离的机器上启动两个程序并让它们进 行通讯。还需记住,计算机的运行速度要比人的操作速度高出许多数量级。在他启动第 一个程序后,该程序开始执行并向对等程序发送消息。在几个微秒内,它便发现对等程 序还不存在,于是就发出一条错误消息,然后退出。此后,他启动了第二个程序。不幸 的是,当第二个程序开始执行时,它也找不到第一个程序(早已退出)。即使这两个程序 连续地重新试着通讯,但由于它们的执行速度那么高,以致于它们在同一瞬间联系上的 概率非常低。在客户机/服务器结构中,服务器在启动后必须(无限期地)等待客户机的 “请求”,因此就形成了“请求——响应”的通讯方式。 在 Internet/Intranet 领域,目前“浏览器—Web 服务器—数据库服务器” 结构是一 种非常流行的客户机/服务器结构,如图 5.6 所示。这种结构最大的优点是:客户机统一 采用浏览器,这不仅让用户使用方便,而且使得客户机端不存在维护的问题。当然,软 件开发布和维护的工作不是自动消失了,而是转移到了 Web 服务器端。在 Web 服务器 端,程序员要用脚本语言编写响应页面。例如用 Microsoft 的 ASP 语言查询数据库服务 器,将结果保存在 Web 页面中,再由浏览器显示出来。 HTTP 请求 查询 HTTP 响应 客户机 服务器 客户机 浏览器 Web 服务器 ASP Engine 数据库 服务器
图5.6“浏览器一Wb服务器一数据库服务器”结构 5.2模块设计 在设计好软件的体系结构后,就已经在宏观上明确了各个模块应具有什么功能,应 放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化 设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。 但是“功能独立”并不意味若模块之间保持绝对的孤立。一个系统要完成某项任务,需 要各个模块相互阳合才能实现,此时模块之间就要讲行信息交流。 比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚 仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸 左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考 虑“这个模块应该怎样与其它模块交流信息”。 本节将论述评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封 闭 一开放性”。 5.2.1信息隐藏 在一节不和诺的课堂里,老师叹气道:“要是坐在后排聊天的同学能象中间打牌的同 学那么安静,就不会影响到前排睡觉的同学。” 这个故事告诉我们,如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可 外扬”就是这个道理。为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在 设计模块时就要注意信总隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏 其它一切内容 模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface), 执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是 个C+对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个CON 对象,那么该模块的公有接口就是COM对象的接口。一个COM对象可以有多个接口, 而每个接口实质上是一些函数的集合。[Rogerson1999 美国也许是世界上丑闻最多的国家,因为它追求民主,不懂得“隐藏信息”。但美 国又是软件产业最发达的国家,模块化设计的方法都是美国人倡导的,他们应该很懂得 “隐藏信总”。真是前后矛盾,这些美国佬! 5.2.2内聚与辋合 内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling》 是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块 通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计 追求强内聚,弱耦合 一、内聚强度 内聚按强度从低到高有以下几种类型: 6
6 图 5.6 “浏览器—Web 服务器—数据库服务器”结构 5.2 模 块 设 计 在设计好软件的体系结构后,就已经在宏观上明确了各个模块应具有什么功能,应 放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化 设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。 但是“功能独立”并不意味着模块之间保持绝对的孤立。一个系统要完成某项任务,需 要各个模块相互配合才能实现,此时模块之间就要进行信息交流。 比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚 仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸 左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考 虑“这个模块应该怎样与其它模块交流信息”。 本节将论述评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封 闭——开放性”。 5.2.1 信息隐藏 在一节不和谐的课堂里,老师叹气道:“要是坐在后排聊天的同学能象中间打牌的同 学那么安静,就不会影响到前排睡觉的同学。” 这个故事告诉我们,如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可 外扬”就是这个道理。为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在 设计模块时就要注意信息隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏 其它一切内容。 模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface), 执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是一 个 C++对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个 COM 对象,那么该模块的公有接口就是 COM 对象的接口。一个 COM 对象可以有多个接口, 而每个接口实质上是一些函数的集合。[Rogerson 1999] 美国也许是世界上丑闻最多的国家,因为它追求民主,不懂得“隐藏信息”。但美 国又是软件产业最发达的国家,模块化设计的方法都是美国人倡导的,他们应该很懂得 “隐藏信息”。真是前后矛盾,这些美国佬! 5.2.2 内聚与耦合 内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling) 是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块 通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计 追求强内聚,弱耦合。 一、内聚强度 内聚按强度从低到高有以下几种类型:
(1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。 (2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模 块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模 块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的 修改也比较困难。 (3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但 这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次 序执行,则称为过程内聚。 (⑤)通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为 通信内聚。 (6)顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出 作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。 二、祸合强度 耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用:(2)一个模 块向另一个模块传递的数据量:(3)一个模块施加到另一个模块的控制的多少:(4)模 块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模 块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合。 一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模 块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。 (4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构 的变化将使相关的模块发生变化。 (5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合。模块间没有信息传递时,属于非直接耦合。 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的 范围,坚决避免使用内容耦合。 52.3封闭一一开放样 如果一个模块可以作为一个独立体被其它程序引用,则称模块具有封闭性。如果 个模块可以被扩”充,则称模块具有开放性 从字面上看,让模块具有“封闭一 一开放性”是矛盾的,但这种特征在软件开发过 程中是客观存在的。当着手一个新问题时,我们很难一次性解决问题。应该先纵观问题 的一些重要方面,同时作好以后补充的准各。因此让模块存在“开放性”并不是坏事情。 “封闭性”也是需要的,因为我们不能等到完全掌握解决问题的信息后再把程序做成别 人能用的模块
7 (1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。 (2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模 块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模 块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的 修改也比较困难。 (3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但 这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次 序执行,则称为过程内聚。 (5)通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为 通信内聚。 (6)顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出 作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。 二、耦合强度 耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用;(2)一个模 块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模 块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模 块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合。一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模 块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。 (4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构 的变化将使相关的模块发生变化。 (5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合。模块间没有信息传递时,属于非直接耦合。 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的 范围,坚决避免使用内容耦合。 5.2.3 封闭——开放性 如果一个模块可以作为一个独立体被其它程序引用,则称模块具有封闭性。如果一 个模块可以被扩充,则称模块具有开放性。 从字面上看,让模块具有“封闭——开放性”是矛盾的,但这种特征在软件开发过 程中是客观存在的。当着手一个新问题时,我们很难一次性解决问题。应该先纵观问题 的一些重要方面,同时作好以后补充的准备。因此让模块存在“开放性”并不是坏事情。 “封闭性”也是需要的,因为我们不能等到完全掌握解决问题的信息后再把程序做成别 人能用的模块
揽块的“封闭一一开放性”实际上对应于软件质量因素中的可复用性和可扩充性 采用面向过程的方法进行程序设计,很难开发出既具有封闭性又具有开放性的模块。采 用面向对象设计方法可以较好地解决这个问间题。 5.3数据结构与算法设计 学会设计数据结构与算法,可以让我们编写出高效率的程序。也许有人要问,在计 算机速度日新月异的今天,为什么还需要高效率的程序? 因为我们的雄心与能力是一起增长的,技术进步最快也快不过人们欲望的增长。计 算速度和存储容量上的革新仅仅提供了处理更复杂问题的有效工具,所以高效率的程序 永远不会过时 设计高效率的程序是基于良好的数据结构与算法,而不是基于编程小技巧。大多数 计算机科学系在设置课程时,都重视学习基本的软件工程原理,以及数据结构与算法设 计。 一般说来,激据结构与算法就是一类数据的表示及其相关的操作(这里算法不是指 数值计算的算法)。从数据表示的观点来看,存储在数组中的一个有序整数表也是一种数 据结构。算法是指对数据结构施加的一些操作,例如对一个线性表进行检索、插入、既 除等操作。 个算法如果能在所要求的资源限制(Resource Constraints)范用内将问题 解决好,则称这个算法是有效率(E笛it)的。例如一个涤源限制可能是“用于存储 数据的内存有限”,或者“允许执行每个子任务所需的时间有限”。一个算法如果比其它 已知算法所需要的资源都少,这个算法也被称为是有效率的。算法的代价(C0st)是指 消耗的资源量。 一般说来,代价是由一个关键资源例如时间或空间来评估的 毋庸置疑,人们编写程序是为了解决问题。只有通过预先分析问题来确定必须达到 的性能目标,才有希望挑选出正确的数据结构。有相当多的程序员忽视了这一分析过程, 而直接选用某一个他们习惯使用的,但是与问题不相称的数据结构,结果设计出 个低 效率的程序。如果使用简单的设计就能够达到性能目标时,选用复杂的数据结构也是没 有道理的。 人们对常用的数据结构与算法的研究已经相当透彻,可以归纳出一些设计原则: (1)每一种数据结构与算法都有其时间、空间的开销和收益。当面临一个新的设计问题 时,设计者要彻底地学握怎样权衡时空开销和算法有效性的方法。这就需要懂得算法分 析的原理,而且还需婴了解所使用的物理介质的特性(例如,数据存储在磁盘上与存储 在内存中,就有不同的考虑)。 (2)与开销和收益有关的是时间一一空间的权衡。通常可以用更大的时间开销来换取空 间的收益,反之亦然。时间 空间的权衡普遍地存在于软件开发的各个阶段中。 (3)程序员应该充分地了解一些常用的数据结构与算法,避免不必要的重复设计工作。 (4)数据结构与算法为应用服务。我们必须先了解应用的需求,再寻找或设计与实际应 用相匹配的数据结构。[Shaffer1998] 8
8 模块的“封闭——开放性”实际上对应于软件质量因素中的可复用性和可扩充性。 采用面向过程的方法进行程序设计,很难开发出既具有封闭性又具有开放性的模块。采 用面向对象设计方法可以较好地解决这个问题。 5.3 数据结构与算法设计 学会设计数据结构与算法,可以让我们编写出高效率的程序。也许有人要问,在计 算机速度日新月异的今天,为什么还需要高效率的程序? 因为我们的雄心与能力是一起增长的,技术进步最快也快不过人们欲望的增长。计 算速度和存储容量上的革新仅仅提供了处理更复杂问题的有效工具,所以高效率的程序 永远不会过时。 设计高效率的程序是基于良好的数据结构与算法,而不是基于编程小技巧。大多数 计算机科学系在设置课程时,都重视学习基本的软件工程原理,以及数据结构与算法设 计。 一般说来,数据结构与算法就是一类数据的表示及其相关的操作(这里算法不是指 数值计算的算法)。从数据表示的观点来看,存储在数组中的一个有序整数表也是一种数 据结构。算法是指对数据结构施加的一些操作,例如对一个线性表进行检索、插入、删 除等操作。一个算法如果能在所要求的资源限制(Resource Constraints)范围内将问题 解决好,则称这个算法是有效率(Efficient)的。例如一个资源限制可能是“用于存储 数据的内存有限”,或者“允许执行每个子任务所需的时间有限”。一个算法如果比其它 已知算法所需要的资源都少,这个算法也被称为是有效率的。算法的代价(Cost)是指 消耗的资源量。一般说来,代价是由一个关键资源例如时间或空间来评估的。 毋庸置疑,人们编写程序是为了解决问题。只有通过预先分析问题来确定必须达到 的性能目标,才有希望挑选出正确的数据结构。有相当多的程序员忽视了这一分析过程, 而直接选用某一个他们习惯使用的,但是与问题不相称的数据结构,结果设计出一个低 效率的程序。如果使用简单的设计就能够达到性能目标时,选用复杂的数据结构也是没 有道理的。 人们对常用的数据结构与算法的研究已经相当透彻,可以归纳出一些设计原则: (1)每一种数据结构与算法都有其时间、空间的开销和收益。当面临一个新的设计问题 时,设计者要彻底地掌握怎样权衡时空开销和算法有效性的方法。这就需要懂得算法分 析的原理,而且还需要了解所使用的物理介质的特性(例如,数据存储在磁盘上与存储 在内存中,就有不同的考虑)。 (2)与开销和收益有关的是时间——空间的权衡。通常可以用更大的时间开销来换取空 间的收益,反之亦然。时间——空间的权衡普遍地存在于软件开发的各个阶段中。 (3)程序员应该充分地了解一些常用的数据结构与算法,避免不必要的重复设计工作。 (4)数据结构与算法为应用服务。我们必须先了解应用的需求,再寻找或设计与实际应 用相匹配的数据结构。[Shaffer 1998]
5.4用户界面设计 某个人总有办法让自己保持心情愉快、信心十足。有一天,他向一名围棋九段和 名乒乓球世界冠军挑战,结果他全胜了。因为他跟围棋九段打乒乓球,跟乒乓球冠军下 围棋。用户界面的编程技术是人们熟悉得不得了的事,我决定讲一讲比较陌生的“用户 界面设计美学”。 有位爱好书画的博士后请我欣赏钢琴演奏会。我从头到尾只听到“叮叮咚咚”的声 音,实在享受不到“高雅”,就请这位朋友指点。他虽然也不橘钢琴,却从欣赏书法的角 度设法解释如何欣赏音乐。可是我既不懂书法也不懂音乐,真是坐立不安。“美”似乎真 的不可言传。我在读本科时,特别喜欢编写用户界面程序,并且常向同学演示、卖弄 我觉得还不过瘾,就写了一篇“用户界面设计美学”的短文林锐991。凡是路过我实验 室的同学都被我速住,被迫听完我得意之极的朗读,茫然者与痛苦者居多。不久我的朗 读便所向披摩,闻声者逃之天天。现在我又把那篇短文摘录至此,请您忍着点吧。 5.4.1界面设计中美的需求与导向作用 人们对美的向往和追求是与生俱有的。显然没有人愿意丑化自己的程序,也没有用 户嗜好丑陋的界面。软件开发者要设计美,用户要享受美,所以界面的美是开发者与用 户的共同需求」 界面美的概念很抽象,以致让人无法说清楚什么是界面的美。但它同时又很现实 以致人人都可以去欣赏和感受界面美,并且挑剔美中之不足。美学不是一种量化的学问, 如果因此而轻视美学指导,必将导致在设计过程中光依赖程序员个人的经验与感觉。由 于程序员接受的教育主要是如何使计算机完成工作,而不是人如何工作,因此仅靠程序 员主观想象设计而成的界面往往得不到大众用户的认可 美的界面能消除用户由感觉引起的乏味、紧张和疲劳(情绪低落),大大提高用户的 工作效率,从而进一步为发挥用户技能和为用户完成任务作出贡献。从人机界面发展历 史与趋势上可以看出人们对界面美的需求,以及美在界面设计中的导向作用。 界面设计已经经历了两个界限分明的时代。第一代是以文本为基础的简单交互,如 常见的命令行,字符菜单等。由于第一代界面考虑人的因素太少,用户兴趣不高。随着 技术的发展,出现了第二代直接操纵的界面。它大量使用图形、语音和其它交互媒介, 充分地考虑了人对美的需求。直接操纵的界面使用视听、触摸等技术,让人可以凭借生 活常识、经历和推理来操纵软件,愉快地完成任务。更高层次的界面甚至模拟了人的生 活空间。例如虚拟现实环培 界面的美充分体现了人机交互作用中人的特性与意图,越来越多的用户将通过具有 吸引力而今人恰快的人机界面与计算机打交道。 5.4.2界面美的内涵 本节从合适性、风格和广义美三个方面论述界面美的内涵 一、界面的合适性
9 5.4 用 户 界 面 设 计 某个人总有办法让自己保持心情愉快、信心十足。有一天,他向一名围棋九段和一 名乒乓球世界冠军挑战,结果他全胜了。因为他跟围棋九段打乒乓球,跟乒乓球冠军下 围棋。用户界面的编程技术是人们熟悉得不得了的事,我决定讲一讲比较陌生的“用户 界面设计美学”。 有位爱好书画的博士后请我欣赏钢琴演奏会。我从头到尾只听到“叮叮咚咚”的声 音,实在享受不到“高雅”,就请这位朋友指点。他虽然也不懂钢琴,却从欣赏书法的角 度设法解释如何欣赏音乐。可是我既不懂书法也不懂音乐,真是坐立不安。“美”似乎真 的不可言传。我在读本科时,特别喜欢编写用户界面程序,并且常向同学演示、卖弄。 我觉得还不过瘾,就写了一篇“用户界面设计美学”的短文[林锐 1997]。凡是路过我实验 室的同学都被我逮住,被迫听完我得意之极的朗读,茫然者与痛苦者居多。不久我的朗 读便所向披糜,闻声者逃之夭夭。现在我又把那篇短文摘录至此,请您忍着点吧。 5.4.1 界面设计中美的需求与导向作用 人们对美的向往和追求是与生俱有的。显然没有人愿意丑化自己的程序,也没有用 户嗜好丑陋的界面。软件开发者要设计美,用户要享受美,所以界面的美是开发者与用 户的共同需求。 界面美的概念很抽象,以致让人无法说清楚什么是界面的美。但它同时又很现实, 以致人人都可以去欣赏和感受界面美,并且挑剔美中之不足。美学不是一种量化的学问, 如果因此而轻视美学指导,必将导致在设计过程中光依赖程序员个人的经验与感觉。由 于程序员接受的教育主要是如何使计算机完成工作,而不是人如何工作,因此仅靠程序 员主观想象设计而成的界面往往得不到大众用户的认可。 美的界面能消除用户由感觉引起的乏味、紧张和疲劳(情绪低落),大大提高用户的 工作效率,从而进一步为发挥用户技能和为用户完成任务作出贡献。从人机界面发展历 史与趋势上可以看出人们对界面美的需求,以及美在界面设计中的导向作用。 界面设计已经经历了两个界限分明的时代。第一代是以文本为基础的简单交互,如 常见的命令行,字符菜单等。由于第一代界面考虑人的因素太少,用户兴趣不高。随着 技术的发展,出现了第二代直接操纵的界面。它大量使用图形、语音和其它交互媒介, 充分地考虑了人对美的需求。直接操纵的界面使用视听、触摸等技术,让人可以凭借生 活常识、经历和推理来操纵软件,愉快地完成任务。更高层次的界面甚至模拟了人的生 活空间,例如虚拟现实环境。 界面的美充分体现了人机交互作用中人的特性与意图,越来越多的用户将通过具有 吸引力而令人愉快的人机界面与计算机打交道。 5.4.2 界面美的内涵 本节从合适性、风格和广义美三个方面论述界面美的内涵。 一、界面的合适性
界面的合话性是指界面是否与软件功能相融洽。如果界面不话合于软件的功能,那 么界面将毫无用处,界面美的内涵就无从谈起。所以界面的合适性是界面美的首要因素 它提醒设计者不要片面追求外观漂亮而导致失真或华而不实。界面的合适性既提倡外美 内秀,又强调恰如其分。 合语性养的界面无疑会混淆软件意图,致使用户产生误解。即使它不损害软件功能 与性能,也会使用户产生不该有的情绪波动。例如一些软件开发者喜欢为其作品加一段 动画演示,以便吸引更多用户的关注。这本是无可非议的,问题在于这演示是否合情合 理。如果运行一个程序,它首先表演一套复杂的动画,在后台演奉雄壮的进行曲,电闪 雷鸣之后出来的却是一个普通的文本编辑器。整个过程让用户置身于云里雾里,而结果 却让用户感到惊谔而不是惊喜。合适性差的界面只会给软件带来厄运。 二、界面的风格 界面的风格有两类,一是“一致性”,二是“个性化”。 商业应用软件的界面设计注重一致性。设计者必须密切注意在相同应用领域中最流 行的软件的界面,必须尊重用户使用这些软件的习惯。例如商业软件习惯于设置℉1健 为帮助热健,如果某个设计者别出心裁地让℉1键成为程序终止的热健,那么在用户渴 望得到帮助而伸手击F1键的一利那,他的工作就此完蛋。相信这个用户“一朝被蛇咬, 十年怕井绳”。 目前流行的软件开发工具如Visual C+、Visual Basic、Delphi、C+Builder、Power Builder等,都能够快速地开发出非常相似的图形用户界面。在Internet/Intranet领域,浏 览器几乎成了唯一的客户机程序,因为用户希望用完全一致的软件来完成千变万化的应 用任务。 在娱乐领域的软件中,有个性化的界面自然比泯然于众的界面更具有吸引力。 便来,计氧机专业人员玩过的状件不数·界面多了,民有和·管经靴海难为水用 的感觉。不过当我看到一个叫Sonique的放音乐的软件时,不禁对其界面的创意啧喷称 赞,忍不住象贴美女像那样把它贴到书中,如图57所示
10 界面的合适性是指界面是否与软件功能相融洽。如果界面不适合于软件的功能,那 么界面将毫无用处,界面美的内涵就无从谈起。所以界面的合适性是界面美的首要因素, 它提醒设计者不要片面追求外观漂亮而导致失真或华而不实。界面的合适性既提倡外美 内秀,又强调恰如其分。 合适性差的界面无疑会混淆软件意图,致使用户产生误解。即使它不损害软件功能 与性能,也会使用户产生不该有的情绪波动。例如一些软件开发者喜欢为其作品加一段 动画演示,以便吸引更多用户的关注。这本是无可非议的,问题在于这演示是否合情合 理。如果运行一个程序,它首先表演一套复杂的动画,在后台演奏雄壮的进行曲,电闪 雷鸣之后出来的却是一个普通的文本编辑器。整个过程让用户置身于云里雾里,而结果 却让用户感到惊谔而不是惊喜。合适性差的界面只会给软件带来厄运。 二、界面的风格 界面的风格有两类,一是“一致性”,二是“个性化”。 商业应用软件的界面设计注重一致性。设计者必须密切注意在相同应用领域中最流 行的软件的界面,必须尊重用户使用这些软件的习惯。例如商业软件习惯于设置 F1 键 为帮助热键,如果某个设计者别出心裁地让 F1 键成为程序终止的热键,那么在用户渴 望得到帮助而伸手击 F1 键的一刹那,他的工作就此完蛋。相信这个用户“一朝被蛇咬, 十年怕井绳”。 目前流行的软件开发工具如 Visual C++、Visual Basic、Delphi、C++ Builder、Power Builder 等,都能够快速地开发出非常相似的图形用户界面。在 Internet/Intranet 领域,浏 览器几乎成了唯一的客户机程序,因为用户希望用完全一致的软件来完成千变万化的应 用任务。 在娱乐领域的软件中,有个性化的界面自然比泯然于众的界面更具有吸引力。一般 说来,计算机专业人员玩过的软件不计其数。界面看多了,真有种“曾经沧海难为水” 的感觉。不过当我看到一个叫 Sonique 的放音乐的软件时,不禁对其界面的创意啧啧称 赞,忍不住象贴美女像那样把它贴到书中,如图 5.7 所示