2005年11月

为什么需要流程?
虽然之前的文章都在谈论软件美学,但是在这篇文章,让我们暂时先丢开这个浪漫传说,回头来看看真实世界的软件项目。公司接案的最大目的是什么呢?当然就是要能赚钱,这也许很俗气,不过却是事实。软件开发的项目有四个重要的变量-成本、时间、质量及规模,如果以企业生存游戏的角度来看,那成本一定是最难控制的变量,而质量是客户坚持的,时间是客户规定的,相对之下,只要能符合客户的业务需求,最容易控制的,我想就是「规模」了。因此我们可以假设在质量及时间不变的情况之下,若受到规模冲击的影响变小,则成本也会下降,而在成本越小的情况下,公司的获利当然也就会越多了。

只要能积极地管理规模变量,团队及客户对于其它三个变量的控制能力也能增加。因为通常客户不会太清楚他们要的是什么东西,真正的规模到那里,当然,这不是他们的错,软件的本质本来就是恒变的需求,而软件项目的开发本来就常处于「一寸以前,才是光明的」的状况,开发团队必需要协助客户,帮助客户找到他们真正的需求,帮助他们厘清可能的风险,一寸一寸地走,一点一点地将整个光明都找出来。

当一个团队千辛万苦地将一个软件项目完成后,最重要的事情除了收钱之外,还有什么呢?答案是「累积与再利用」,如果你是老板,你会不会希望看到下次在接到相似类型的项目时,开发团队可以利用更少的时间完成项目呢?因此,如何让团队能够在每个案子产生一个正向的回馈,让下次同类型的项目可以减低规模的冲击,进而减少成本,最后提高项目的利润,是一个非常重要的课题,而软件组件重用(Reuse)的技术也就呼之欲出了,这时,以Pattern为基础,并且以软件架构为中心的开发方式将是关键。这整个过程,势必需要一个软件开发流程来指挥协调。

各式软件开发流程
最常见的软件开发流程就是线性或「瀑布式」的旧式流程,当然还有相当有名的XP(Extreme Programming)以及我们要讨论的UP(Unified Process)等等。

软件开发的方法论有很多,各有其巧妙及适合的时机,端看开发团队的能力、项目类型甚至是文化国情。笔者就曾在某大电信公司,遇过某个开发团队所采用的是「课长流程」(就是以该单位主管心中的喜恶为主的「无定向」开发方式)!相信这在台湾也算是一种流行的开发流程吧。

「瀑布式」的旧式开发流程常见的做法依序是收集需求、系统分析、系统设计,最后根据设计结果来实作。这会衍生出一些职位如系统分析师(SA)、系统设计师(SD)以及「最底层」的程序员(Programmer),因此除了开发步骤的每一个阶段是线性的走向之外,时常连开发工作也会依职位的高低而变成官僚作风。有时在同一开发团队的SA甚至会做出和Programmer实作不同的需求文件,因为真正的需求及设计最后是靠SD,甚至是 Programmer以及客户的「提醒」才找出来的。如此的开发方式,不容易应付恒变需求的软件项目,更不容易累积可再利用的软件组件,而开发工作的责任及角色虽然和经验累积有关,但并不全然和职位的线性关系划上等号,因为每一种角色都有其专门的责任及技术深度。

UP精神
「统一流程(unified process)」对于「管理规模变量」以及「提高软件组件的重用」有着不错的影响,而针对线性流程无法确实掌握需求与实作关系的缺点,也有不错的解决方式。我们暂时抛开在流程里常会令人心烦的工作项目及开发阶段,先体会一下UP精神,这将有助于UP的实作。

UP精神最主要围绕在三个方向,分别是Use-Case-Driven、Architecture-Centric以及Iterative & Incremental。以下我先简述其重要观念,针对工作流程及项目的协调运作,将在下一篇的文章提及,而关于UP的详细内容,笔者建议大家阅读「The Unified Software Development Process」这本书。

所谓的Use Case「Driven」,就是指团队在执行整个流程时,都是从Use Case开始所有的工作项目。这样可以让开发团队在每次的工作循环里,以聚焦在客户的需求为出发点,避免迷失在项目的规模中。Use Case是大家用来发掘并记录功能性需求的一种技术。换句话说,它的功用在于找出对使用系统的参与者有价值的需求,并且能产生有价值及看的到的结果;例如,买机票、处理退货、结帐等等。而Use Case也可以帮助我们开发使用者界面,让客户清楚他们未来要如何使用系统来完成他们的工作,帮助客户找出他们真正要的东西,也帮助大家控制「规模变量」。

Architecture-Centric的概念最容易被想要实行UP的开发团队遗忘。通常大家只会注意到UP所定义出来繁复的工作项目,并拘泥在工作项目的意义及执行时间点,殊不知以软件架构为中心的开发观念是UP在执行对象导向项目的支柱。

软件架构的功用及开发步骤在之前的几篇文章之中已经提过了,在此我只再强调其对于「提高软件组件的重用」及「提供稳定的开发基础」有着重要的影响。在Use Case Driven的观念下,Architecture的开发是以支撑Use Case需求为目的,而Architecture的建构则会影响到那些Use Case可以被实现。

Iterative & Incremental意谓着透过回馈与调整来拥抱改变。在软件需求的恒变性之下,我们需要反复式的开发,让软件可以在一连串的建构、回馈与调整的循环之中逐步完成,这是线性式的开发流程所欠缺的。而这种短期、固定时间长度以及小规模的开发循环之下,可以让软件开发的复杂性变小,我们也可以用Risk- Driven的观念,先开发风险比较高的部份,让早期的进展减缓风险可能带来的冲击。

大部份的人也许会觉得UP是一部很笨重的方法论,拿来做研究还不错,但是对于用在实际的项目上,则是望之却步。其实在了解UP的精神后,再找出自己团队或者项目适合的开发活动与工作流程才是真正的关键。UP其实可以很敏捷,也可以很繁重,端看团队真正的需求。


我们都知道,盖房子都会有一个挖地基,打地基的步骤,而且依据房子的类型及用途,地基的工程以及结构的材料也会有所不同,这些都是经过建筑师专业而仔细的设计及计算才得知的,为的是房子未来的安全及使用需求,甚至是施工过程的安全及顺畅度等等。软件建造的道理,其实是一样的,大家也都希望不仅开工顺利,最后也验收顺利,最好是在保固期内,什么事都不会发生。

建立软件架构的目的,就在于提供系统一个稳固而坚实的发展基础,而其具体的产出,就是一份软件架构书以及依照架构书这个最高设计原则所建构出来的原型(Prototype)。这样就可以让后续加入的工程团队遵循架构设计所表达的美感,并照着原型的建造工法,来继续大部份尚未完成的工程。

当你决定使用以架构为中心的开发方式时,这两个产出是连接架构设计阶段及实作阶段最重要的东西。当然,软件架构的设计不是一、两篇文章就可以解释清楚的,本文仅将一些比较重要的概念及经验提出来,希望能给初入门的读者有个概括的认识。

所有的故事,都是从需求开始
任何一个软件项目的开始,都是为了满足某些特定的需求,这是一句废话,但也是一句非常重要的话,因为当我们在塑造软件架构的时候,显性的需求或其所带来的隐性特质,都可能是影响日后系统成功的关键。显性的需求是客户最直接的期望,他可以明确的告诉你说,他需要在某个画面上加一个按钮,让使用者可以简单的按下按钮,就轻松地订到机票并且自动地完成付款,但是客户可能不会清楚使用者在按下这颗按钮时,不希望等待时间超过10秒钟,他更不会知道从按下按钮,到系统完成所有的工作,是经过几万行的程序并和几个外部系统联机后才会有的结果。而一百或者一千个使用者的规模,对于软件架构以及开发成本,又有着截然不同的设计,往往客户和开发人员的期待落差,就此产生。因此,影响软件项目成败的地方,大部份不是显性的需求,而是隐藏在那背后的邪恶灵魂。

透视需求,找到架构观点
不知道各位有没有做过健康检查?当你在健检中心做检查时,通常会有好多的关卡,而您会像过关斩将般地面对一站一站不同的仪器及医生,每一站所检查的内容都不一样,例如骨科医生看到的,是你的骨质状况;肠胃科看的是你的肠胃健康,他们唯一做的同一件事,就是在检查同一个你。

人类是一个完美而复杂的系统,人体的复杂当然不是只有我们看的到的外表,这是我们都能了解的事实,而软件系统也是一样。以UP(Unified Process)这个软件开发方法论为例,它有几种架构观点(views);使用案例观点、逻辑观点、执行观点、部署观点、实作观点等等。这些软件的观点,就像是不同科的医生对人所看到的不同观点一样,都是在描述同一个软件系统。

设计架构的工作,其实就像医生角色伴演的游戏,你要伴演各科别的医生,透视软件系统,从外在功能面上的需求,找出隐含在各种观点背后的需求,然后再像建筑师,设计出满足这些需求的架构。
显性的需求通常所描述的是「使用案例」(Use-Case),这是以使用者的观点来看系统的外观长相,看的到的东西都是属于功能性的需求,例如,会员管理、票价查询、订票等等。

在这些显性需求背后所含的隐性特质,就是所谓的SLR(Service Level Requirement),其中包含了可靠性(availability)、可修改性(modifiability)、效能(performance)、安全(security)、可测试性(testability)、可使用性(usability)等等,而满足这些从使用案例观点所发觉出来的SLR,就是「逻辑观点」以及其它的架构观点的责任了。

满足需求再描述设计
在满足SLR及设计架构观点的过程中,我比较建议从「逻辑观点」开始,因为它是在描述软件实作时所需的设计概念。在这个观点里,会有较高层次的设计组件,包括Package、Subsystem、Interface等等,它会直接影响到执行观点以及实作观点的设计,当然,部署观点也是一样。只不过通常部署观点会有需多客户本身的限制,例如,客户需要用掉之前的机器库存或者预算限制等等,因此在项目开始前,这个部份有时是被限制住的。其实这些架构观点都会因为某些需求而互相影响,端看架构师的智慧及经验来取舍。

对于这些架构观点详细的说明或举例,因为篇幅的关系,建议各位去读读UP相关书籍及Craig Larman所著的《Applying UML and Patterns》一书,大家会有更清楚的了解。

「凡走过,必留下痕迹」,以上所说的种种观点,如果没有记录下来,架构的设计也就没有意义了,一般来说,我们会将这些设计内容一一地写到所谓的 SAD(Software Architecture Document)-软件架构书里,这是系统未来开发的最高指导原则。而为了系统后续的开发顺利,在SAD完成后,我们还必须分析并找出架构设计里面的指标因子,例如风险较高的技术、连接子系统的关键、影响程序撰写风格的部份等等,然后进行细部设计及实作这些指标因子,使成为所谓的Prototype。接下来,开发团队的大部份成员就可以遵循着SAD及Prototype的脚步,比较稳健而安全的开发下去。

用Pattern的美感来表达软件架构
Pattern是一个很重要的软件设计技术,从定义中我们可以知道它是在一个特定背景下,已被命名而且为了解决某个特定问题的解决方案,而 Architectural Pattern更是针对架构性的问题所发展出来的,例如在POSA1所提的,Layers、Broker、MVC等等。现在世面上所流行的技术或Open Source Framework,像是RMI、Struts等等,都是基于上述的Architectural Pattern所发展出来,当然,在做架构设计时,reuse这些技术框架也是省时方便的策略。

一般的软件技术,如OO的基本原则,或者系统分析设计方法并不一定能真正地解决特定的SLR,因此这些已验证过的Pattern可以做为设计软件架构时,满足特定隐性需求最佳的架构单位,除了利用Pattern来建构一个稳固的软件架构之外,其利于沟通的特性让你容易地将你的设计记录在SAD里并让后续的开发团队易于了解。让自己的「创意」经由「沟通」而能确实实现,我想是这是每个软件人的希望吧。

对于这些架构观点详细的说明或举例,建议各位去读读UP相关书籍及Craig Larman所著的《Applying UML and Patterns》一书,大家会有更清楚的了解。


「架构」一词意味着什么呢?这个看似学术的名词,其实在我们的日常生活中也常会听到的,例如,文章架构、组织架构、发展架构,它在形容事物的一种风格、组织方式或着决策方向。在这里我们要谈的是软件架构,顾名思义它也是指软件的一种风格、组织方式或着决策方向,不过这样的解释可能不好理解,毕竟软件本身是一个复杂的创意。软件架构的重要,就如同每个人的体质,从身体的最深处,一层一层的影响到外表的美丽。

软件架构杂乱,项目失败风险高
我曾经历过一个经典且传统的软件项目,那是一个旅游网站,旅客可以在网站上透过聪明的航程规画精灵实时地比较机票的价格,然后直接订机票并且在线付款,流程中几乎不用和旅行社的票务人员交涉,除此之外,在那个网站上也可以查到各式旅行信息。

和大部份的软件项目一样,在这个案子里有一位野心勃勃的老板,另外,我们有一些极热心但是拥有模拟脑(完全不懂计算机)的旅游专家──就是票务小姐及票务经理,然后我们需要和信用卡系统及一个世界级,但是稳定度及整合接口是古迹级的票务系统介接,最后还有一群从没搭过飞机,但视加班为进补的程序开发人员,我们要做的系统包含了旅客会直接使用的订票网、票务小姐管理票务的后台系统、跨平台介接的实时订票系统及在线付款系统等等。

这个项目包含了许多不同的「角色」及「需求」。角色有旅客、老板、票务人员及软件开发人员等等。他们都有各自己的需求,例如,旅客希望票价便宜并且系统使用安全、方便及稳定、老板希望以最少的成本及时间就能上线、票务小姐希望系统能减轻她们后台票务的工作量、营销业务希望什么功能都有、开发人员希望系统设计弹性或者使用熟悉的技术来开发。

于是乎,开发人员为了符合大家的需求或者某个特殊活动的需要,开始了一夜夜悲壮的软件开发战役。而在系统尚未稳定之时,没想到老板竟想要复制「成功」经验,开始建立两岸三地的作战堡垒,因此系统也由小恐龙渐渐地变成了究极大怪兽。

「使用接口风格不一致、订票系统容易断线、订票响应速度太慢……」,一连串的问题陆续出现,然而最终没有人真正了解问题的来源或者可以解释细节。这个没有人能控制的大怪兽终于带来了最后的审判日──失败的软件项目。这个项目失败的原因很复杂,包括了使用者接受度、商业模式(Business Model)、政治因素、项目管理控制等等,当然有很大的一部分原因是:它拥有一个杂乱的软件架构及一个无法控制的系统。

软件系统的最高指导原则
所谓的软件架构,简单的来说,就是定义一个软件系统未来发展的「最高指导原则」,就像宪法一样,在未来的发展中不可抵触,例如,系统该分成哪些模块、要用哪些技术、组件间的沟通接口是什么、组件间的互动行为要用哪些Pattern。

这些最高的原则当然是由各种需求或者开发团队的能力分组所引导出来的,例如,分成三层,并分给三个不同能力的小组来完成,或者使用讯息中介软件介接外部系统,缩短使用者的等待时间并增加系统的稳定度及可靠度等等。这些最高指导原则订定了整个软件系统发展的大方向,也等于限制住系统技术发展的最大范围,进而防止系统开发走向错误的方向,并且让开发人员有一个技术的依归,可以踏实地完成系统开发。当然,宪法是可以修的,只不过必须经过正式的修宪会议而已,软件架构也是一样,不然在偏离架构范畴下所开发出来的软件,最后还是很可能会误入歧途而失败。

在《Software Architecture in Practice》一书中,作者Bass、 Clements、Kazman对于软件架构以比较学术的方式来解释:「软件架构是一种系统的结构,「包含」并「定义」了许多软件组件及其间的组织方式和互动行为」。换句话来说,软件架构有许多软件组件、一个以上的结构以及它们之间的各种关系。除此之外,架构还包含了非功能面的条件,像是QoS (Quality of Service)或是SLR(Service Level Requirement),它包含可用性(Availability)、可信赖(Reliability)、可扩充(Scalability)和安全性(Security)等等。
上述这个定义也暗示着任何一个软件系统都有软件架构,只不过和系统本身的好坏无关。对于一个软件系统来说,架构并没有绝对的好与坏,只有适不适合而已。例如,一个时间紧迫并且规模小的网站系统,就没有必要一定要用MVC的架构来开发,它也可以很简单的利用Layer的观念并且配合资料封装技术来实作。建立软件架构的关键在于能够提供软件系统未来实用而稳固的发展基础,这是很重要的一点,因此架构不应由错误尝试中得到,它应该是经过认真的设计及规划出来的,而架构文件则是架构设计后所该留下的重要结果,也是未来开发的依据。清楚的架构设计有利于开发过程的「沟通」、系统的「演化」以及未来产品线开发的「再利用」。

平衡软件项目元素,建立扎实软件架构
通常一个软件系统,打从娘胎开始就会不断地受到各种需求的影响,但同时满足所有的需求是不容易的,因为有许多的需求是相互抵触或者是有隐含的意义。软件的「开发过程」就是在协调及满足这些需求,而过程中,如果没有好好地规画软件设计的发展策略,软件系统很容易会变成多头马车,进而演变出迭床架屋的架构,充满随时倒塌的危险。

架构师的责任就在于利用自己的经验来「平衡」项目相关人员需求、开发团队技术能力、技术环境及成本等元素,进而建立出扎实的软件架构。虽然我们都知道软件的本质是「恒变的需求」,但是在我们拥抱改变的信念下,还是不可能从咖啡机中煮出红酒来的。
「软件架构」就像是一个人的体质,它会影响软件系统未来的生老病死,因此在开发软件系统前,请先好好的调理它。


一个好的软件系统,除了必须符合使用者的需求之外,应该可以容易地再利用、维护及扩充。要达成这个目标,需要多少的设计思维及创意呢?若是符合这个目标的系统,一定充满软件美学的精神。软件设计其实是一种艺术,它不仅需要灵活的设计思维及技巧,还需要平衡项目的资源及使用者的期望,因此不管从工程面来看,还是从管理面来看,都是一种艺术。而要秉持软件美学精神来开发软件系统,其最深层的根基则必须要以「Pattern」来支撑。

相信有许多软件开发人员会觉得:Pattern和自己开发的程序代码好像是两回事一样,不知如何理解及使用,更不会了解它和软件架构、开发流程,乃至于整个开发项目的关系。美丽,应该从头开始,开发一个好的软件系统也是一样,就让我们从敷Pattern面膜开始。

我们先想象一个情境,假设你是一家比萨餐厅的大厨,现在有两位客人向你点餐,第一个人说:「我要一份虾仁搭配菠萝、洋葱的比萨,虾仁要多一点,再加上一份洒上墨西哥辣椒、洋葱、美式腊肠、意大利肉肠的比萨,不要太辣,外加一份法国香蒜面包及2瓶350毫升的百事可乐」。第二个人则说:「鲜虾菠萝及哈辣墨西哥比萨各一份,外加一份香蒜面包及2瓶小瓶的百事」。

如果你是大厨,你会比较希望听到哪一种点餐方式?除此,这两个人的点餐方式有什么差别呢?其中最大的差别就在于第二个人使用了大家都了解的餐点名称,大大地简化了点餐所使用的语句,而且所表达的意思和第一个人所要的差不多。如此身为大厨的你不仅容易理解顾客所点的餐点,也容易记忆他要的东西,并且节省了彼此沟通的时间。相较之下,你听到第一种点餐方式是不是很想把他轰出餐厅呢?

Pattern的沟通力量
我们再来看看对象导向的程序设计。一个设计老手一定熟悉许多对象导向的基本原理及设计原则,当他在解决一些设计问题时,会很自然地运用这些技巧及经验,设计出解决各式难题的蓝图。

假设你就是这位设计老手,而你刚好要跟伙伴说明这个绝妙的设计。你可能会说:「我先建立一个super abstract class及两个继承于它的sub-class,并在其中一个sub-class中建立一个往super abstract class方向的association以便形成container功能,这样就可以先完成一个递归的类别结构,然后在那container class实作一个final的method,用来固定的呼叫super abstract class所定义的抽象方法,这样在未来继承于此container class的类别阶层的所有对象,都可以动态地被加入到执行时期对象模型的递归结构,透过分别修饰原有功能来增加新功能」,不知道说到这里,有几个人听得懂的呢?

或许你可以用更简洁的方式来表达,你可以说:「在这个部份的设计,我要建立一个Decorator Pattern」。一句再简单不过的话,就足以表达上述那段又臭又长的演讲,甚至所传达的意思还更为清楚。这就是「共享词汇」的力量,也是「沟通」的力量。

在前一篇「感受美丽的元素,设计思维」中有提到,大部分的软件开发都是团队合作,要让大家了解自己伟大的设计,光用言语及UML来表达可能不够,还必须注意传达设计思维的问题。由于每一个Pattern的设计都是基于对象导向精神及原则,在应用上来看,它们保证了软件某个程度的弹性及稳定,除此之外,「沟通」则是另一项非常重要但却比较少人体认到的功用。从Pattern的定义来看:「一种在某个特定情境下,为了解决某种问题的解决方案。」因此,还要再加上某个特定「名称」,才能算是个Pattern,才能和别人沟通。

3类常见的Pattern
Pattern有很多种类,只要符合定义的都可以称做Pattern,但我们先把Pattern简单的分为三种常见的种类,分别是「Architectural Pattern」、「Platform Pattern」及「Design Pattern」,代表三种不同层次的应用,因为每个层次都有各自不同的问题。

Architectural Pattern针对的是软件架构,如Pipes、Broker、MVC等等。

Platform Pattern针对的是某个特定的发展平台,例如J2EE Pattern。

而Design Pattern就是大家比较耳熟能详的,例如四人帮(Gang of Four,简称GoF,这四个人分别是Gamma、Johnson、Helm、Vlissides)的圣经级著作《Design Patterns: Elements of Reusable Object-Oriented Software》一书中,所探讨的是针对一般化的问题,而不是特定领域的问题。每个领域的Pattern几乎都可以看到Design Pattern的影子,因为它提供了最基础而稳固的设计元素。

看到这里也许有人会说,有那么多开放原始码软件可用,为何还要用Pattern呢?这其实也是个事实,我们常常寻找好用的开放原始码 Framework作为开发系统的骨干,例如,Struts、Spring、Hibernate等等,通常我们只要加上一些客户需求的程序代码,拼凑一番,就可以上线了,似乎用不上Pattern。但是,除了要注意开放原始码的授权种类之外,还要注意那些Framework,是否有助于我们让自己的程序代码容易地维护、再利用及扩充?是否有助于我们去「沟通」?再者,这些著名的Framework都是使用Pattern来设计的最佳范例,因此理解 Pattern对于了解Framework的精髓,及如何整合入自己的系统,有很大的帮助,才不会误解Framework的整合方法而张冠李戴,这就是使用Pattern的最好时机及理由。

必学23个Design Pattern
初学Pattern的人一定会觉得Design Pattern很难放到自己的设计上,这个因扰大部分来自于所学的Design Pattern太少,只学习了几个Pattern就急于使用,就很容易会为了使用Pattern而强用Pattern,反而是化简为繁。因此,首先要尽量让自己对于Pattern有多一点的理解。我认为GoF所归纳出来的23个Pattern,是你该理解的最基本数量,这么说并不代表在设计软件中就应该全数用上,重点是当你理解那23个Pattern后,对对象导向会更有感觉,所累积的原力会让你较容易地「感觉」出,应该使用哪些Pattern。其实在做程序代码的Refactorying时,是最好的练习时机,因为你可以从现有程序代码,慢慢修改到某个Pattern的形式,要记住的是,Pattern只是心法,如何变化出招式是靠你的美感,实际的设计是没有必要和Pattern所定义的形式完全相同的。


世界上大大小小的事件、每个人的想法、做法,只要不是具象的事物,就都是抽象的,而软件设计的思维亦是如此。

在软件设计的领域中,对象导向的设计方式是个抽象的分析思考法,很适合在虚拟世界里面表达出真实世界的需求。虽然对象导向的技术已经行之有年了,但是真正了解其观念,并且能将其思维放到实际设计与实作上的人并不多,可能是因为受到现实环境的影响。

以Java来说,笔者就看过许多使用Java写出C程序风格的例子,以应用面来看,这样实在谈不上有设计思维,只能说是透过程序撰写一行行地列出工作而已。然而也许是因为这样的观念不清,导致利用Java开发的系统效率不张或者幽灵问题不断,而让人误解Java系统的好坏。

对象导向的关键哲理-抽象
对象导向是一种很有趣的分析「哲学」,它试图从人们对真实世界的看法及感受来解释属于虚拟世界的软件。从分析问题领域,进而转成相对应的程序代码,都是在模拟真实世界里的自然过程。

真实世界的事物是瞬息万变的,而软件开发也是如此,唯一不变的,就是恒变的需求。因而,「抽象」则是描述「瞬息万变」的一个重要并且安全的方法。

抽象的观念也是大部分的人在学习或运用对象导向观念时,比较容易遇到的瓶颈。所谓的抽象,简单来说,就是「萃取」出的概念,而且和实作无关,因为真实问题背后所隐含的意义,往往无法从直接的实作中得知。抽象的焦点是事物的本质特性,不同的观点可以对同一事物抽离出不同的元素来,若是将抽象结果对映到真实世界中,有可能是一个具体的东西,也可能是一个看不见的概念。

抽象有助概念与实作的再利用
想象一个简单的例子,在人声鼎沸的世贸计算机展里,你能够抽象出什么呢?是某计算机公司的摊位、辣妹跳舞、某个品牌的计算机,还是拆解成摊位、辣妹、表演、消费者、产品、销售、交易?若是后者,那么将这些元素套用到家具展上,其实也没问题。因此,抽象有助于「概念的再利用」,同样也有助于「实作的再利用」及稳定。

如果我们要设计一个展览馆的软件平台,起码,当计算机展要改成家具展时,只要重新实作产品内容,不用重新设计整个平台,因为交易模式及展埸运作模式都是类似的。

大家在此应先建立一个观念,对象导向的分析方法是由特殊到一般,由实体到抽象。抽象的观念还可以在经过一般化(generalization)或者特殊化(specialization)的分析后,衍生出阶层的结构,这一层一层的抽象结构,可以像一层一层的防火墙一样,挡住一级一级需求变异的风浪。

以黑客任务说明对象导向
对对象导向技术有初步认识人都会读到不少技术名词,例如接口(Interface)、讯息(Message)、类别(Class)、抽象(Abstraction)、具象(Concrete)或者Pattern、IoC(Inversion of Control)、Dependency Injection、MVC(Model View Controller)等等,这些名词都和对象导向的设计思维习习相关。我很喜欢的一部电影──黑客任务(Matrix),可以套用来说明这些名词的观念及对象导向设计的思维。

在黑客任务的电影情节里,人类被所谓的「母体」控制在一个虚拟的计算机世界里,在那个虚拟世界里面的所有事物,例如行道树、食物、味觉,甚至物理或化学反应,不管是看得到、感觉得到还是所谓的自然定律,都是计算机程序所创造出来的,而真实的人类却浑然不知地被「饲养」在一座座充满连接管线的生态槽里。

看完电影后我不禁思考:如果我是那母体(Matrix)的架构师(architect),该怎么设计那套系统?

我大概会利用复合技术(composition)的方法,再配合连结器(adapter)机制和真实世界生态槽里收集人体各项模拟讯号的生物接口连接,再转化成数字讯号,然后将人类的类别(class)具现(instantiate)到数字世界里时,利用dependency injection的方式,将经转化的数字讯号注入人类实例(instance),并和那里的所有事物建立对象关系,然后人类就可以在那个世界里生活着。

这么酷的想象,透过对象导向的技术来形容似乎就不难理解,它可以容易地传递设计的思维。然而,光是了解对象导向的原理,还不足以让你变成一位好的设计师,好的设计作品应该是容易地再利用、扩充及维护的,因此除了抽象、封装、继承与多型的基础概念之外,有经验的老手可能还知道许多对象导向的设计原则,像是「封装变异」、使用复合技术代替继承、针对接口写而不是实作。

设计思维的传达与再利用
当你在利用上述这些设计思维来塑造软件时,出现的另一个问题是:大部分的软件开发不会是只由你一个人负责,而是团队作业,然而在一个团队中,要如何让伙伴了解你根据这些对象导向基础或原则所做的美妙设计呢?设计思维的传达,变成了下一个问题,而这也意味着一个很重要的概念,那就是设计思维应该是可以再利用的。

对象导向的重要概念-接口
在对象导向的设计里,表达抽象的一个重要设计概念就是「接口」,它也是对象导向的基石。这里所指的界面有2种意义:Java语言上的接口(interface),以及概念上的接口,此处我们所谈论的是后者──概念上的界面。

对象的接口界定出「外界」能送给它的讯息信息,也可以说是「限制」了外界对该对象所能影响的范围,对每个对象来说,接口也是了解另一个对象的唯一方法。以汽车的例子来说,我们看得到汽车的外观,也看得到汽车移动的方式,因此汽车的外观及移动方式,都是属于汽车的接口。

至于汽车是何以能如此移动,是利用什样的动力驱动-汽油引擎、柴油引擎或油电混合引擎,是采用什么样的传动机制-前轮驱动、后轮驱动或四轮驱动,以上这些则属于实作。

有趣的是,一个对象可以有一个以上的接口,这就是多形(polymorphism)的应用。举个例子来说,台湾之光-王建民,他是洋基队的球员,他也是台湾人,更是他父母的儿子,因此,以球队的观点来看,他可以帮洋基队投球,以台湾的观点来看,他有国民的义务,以家庭的观点来看,他必须尽到做儿子的责任,但是他都是王建民。所以,接口有点像是对象所扮演的角色,而角色当然就伴随着「责任」,对象导向设计时的重点之一就是在做对象的「责任分配」。

有许多的Pattern或者Framework都要靠稳定的接口为其发展的基础的,例如,DI(Dependency Injection)、MVC(Model View Controller)、Spring、Struts、JUnit等等,不胜枚举。