转自IBM developerWorks,想和大家探讨一下有关模式和系统架构方面的内容,欢迎讨论
面向模式构建系统架构
邓辉、孙鸣
架构是一个软件系统中的核心元素,是系统中最难改变的部分,也是构建软件系统中其他部分所依赖的基础,因此系统架构的好坏会从根本上决定基于这个架构所构建的软件系统的质量。系统架构的构建一直是软件开发过程中的一项重要工作,同时也是一项很困难的工作,即便对于很有经验的系统架构师也是如此。但是,模式以及模式语言的提出给出了一条构建系统架构的有效途径,本文将对此进行深入的论述,并以一个著名的单元测试工具JUnit为例进行说明。
概念分析
首先,对架构(architecture)和模式(pattern)这两个概念进行简单的介绍。前面已经讲过,架构是一个系统中的核心元素,是该系统中最本质的部分。系统的各个组成部分正是通过架构所描绘的方式协同工作共同完成系统的功能,从而表现出一个完整的系统。由于系统的本质是不容易变化的,所以如果一个架构构建的正确,也就是说能够真实的反映出系统的本质,那么就可以使基于该架构构建的系统具有比较长的生命力,否则该系统的质量就会逐渐的降级,直至崩溃。如果以建筑领域来作类比可能会比较好理解一些,试想如果一个建筑物构建在一个在结构上有问题的混凝土骨架上,那么该建筑的寿命可想而知是不会长久的。
什么是模式呢?模式是在某种特定的场景(context)下某个不断重复出现的问题的解决方案。模式本身并没有任何的创新性,它仅仅是对于一些已经被证明为优秀的解决方法的归类、总结,目的是为了重用该解决方案而又不用做重复的劳动。其中,"特定场景"和"重复"两个限定词非常关键,"特定场景"给出了什么时候以及为什么使用一个模式,"重复"说明了模式的可重复性从而可以被重用。参考文献〔4〕中总结了23个经典模式,非常值得读者朋友细心研读。
架构搭建的难点
既然架构是一个系统中最核心、最本质的部分,要想构建一个正确的架构当然不会容易,为什么呢?下面将从几个方面进行分析。
首先,由于架构是一个系统中的本质的反映,因此架构一般是由系统中不容易改变的部分组成,这些不容易改变的部分往往是系统中的一些抽象的概念。但是,我们在构建系统的架构前仅仅有一些用户的需求以及对于用户业务流程的用例(use case),如果缺乏有效的指导原则,很难从这些信息中提取出反映问题领域本质的概念来。关于这一点,参考文献〔3〕中给出了一个很有效的方法:Commonality/Variability(公共性/可变性)分析。它的核心思想是针对问题领域中的概念进行分类,把那些看上去不同但本质上属于一类的概念用一个抽象的概念来表示,然后基于这些抽象的概念构建架构。Commonality/Variability分析方法在发现系统中的抽象概念方面确实很有效,但是前面已经说过,系统架构不仅仅是一些系统中的抽象概念,而且还描绘了这些抽象概念间的关系,仅仅使用Commonality/Variability分析方法不能十分有效的发现抽象概念间的关系。
其次,即使确定了抽象概念间的关系,构建起了一个系统架构,在这个架构的指引下,我们还是很有可能陷入困境。为什么呢?因为该架构可能会缺乏对于我们进一步工作的指引,缺乏后续发现对象的有效手段。也就是说,架构可能过于空泛,缺乏延伸性。
最后,一个经验丰富的系统架构师可能成功的构建了一个系统的架构,成功的基于该架构完成了系统,但是他的经验、心得体会可能会很难为其他的系统架构师所理解,从而在解决同类问题时可以有效的被重用。因为,这些心得体会如果表现的过于抽象,可能会显的空泛从而指导意义不大;如果表现的过于具体,有可能陷入细节从而失去通用性。
正是由于上述原因使得系统架构的构建非常的困难,往往必须由资深的系统架构师来完成。但是随着模式的提出以及对模式研究的深入,这种局面正在逐步的改变。下面让我们先来对模式进行一番深入的认识。
深入认识模式
读者对于模式的认识大都是从《设计模式》(由参考文献[4]翻译而来的)一书的出版开始的。该书中描绘了23个经典的模式,每一个模式都给出了一个精制优雅的解决方案,为每一个程序员所沉迷、拍案叫绝。但是由于经验的不足往往会造成对于模式的误解。《设计模式》一书的作者John Vlissides为此专门写了一片文章进行阐述,详见:http://www.research.ibm.com/designpatterns/pubs/top10misc.pdf。本人在对模式进行深入的研究以及实践的基础上,把对于模式的认识划分为三种境界,下面一一进行说明。
第一层境界:认为模式就是一种解决方案,一般刚刚接触模式的人都处于这个认识层面。确实是这样,刚刚见到模式时很容易被它所提供的精制的解决方案所吸引,由于缺少经验反而容易忽略模式的其它的重要方面。这样做非常容易造成对于模式的误解、误用。比如:为了使用模式而使用模式以及过分的使用模式等。这种境界仅仅认识到模式怎样(How)去解决一个问题。
第二层境界:除了解模式提供的解决方案以外,还能够认识到模式背后的一些指导原则。《设计模式》一书的第一章对于模式背后的一些指导原则进行了详细的论述,比如:针对接口编程;优先使用组合而不是继承;发现变化、封装变化等。不过缺乏经验的人可能对此理解不会深刻,甚至忽略了这些原则,需要不断的实践、体会方能理解。不过体会到这些原则之后就会对面向对象的思想的理解提升一个层次,提高自己的面向对象分析、设计能力。参考文献[2]中对于这些原则有深入细致的讲解,相信会对读者有非常大的帮助。
第三层境界:认识到模式其实是一些关系,用来舒缓系统内部的"冲突力"。这一点仅仅从模式提供的解决方案实例中就可以看出。其实,不管你如何去做,最终的解决方案肯定是一些模块通过彼此的交互、建立一种关系来完成所需的功能。为什么模式提出的方法就是好的呢?就是因为模式给出的关系舒缓了系统内部的"冲突力",使人感受上去舒服一些。另外,模式并不是简单的给出了一些概念间的关系,它进一步为我们提供了一个场景,提供了进一步工作的指导。是不是太抽象了,我们来举一个例子来说明一下:面向对象设计中有一个著名的原则:SRP(Single Responsibility Principle),即一个类仅仅需要一个职责。如果一个类有多于一个的职责有问题吗?实现起来当然没有问题,但是经验丰富者马上就会感觉到职责间关系的这种"冲突力",从而感觉到不舒服。反映到实现层面上就是本来毫无关系的模块间依赖过于强,导致代码僵化,牵一发而动全身。"高内聚,松耦合"堪称软件领域处理模块间关系的首要指导原则,但是它没有告诉我们对于一个问题具体该如何去做,模式的出现填充了这个空白,如果我们知道一个模式可以解决我们手头的问题,那么我们就可以非常感性的认识到如何达到"高内聚,松耦合",因为模式已经给出了这些关系。这一层境界非常抽象,很难达到,有兴趣的读者可以阅读参考文献[1],相信会有更进一步的认识,更加深入的理解。
下面我们就来谈谈模式为什么能够为系统架构的构建带来有效的帮助。
面向模式构建系统架构
前面介绍了构建系统架构时的难点,本小节将讲述一下模式如何能够有效的解决这些难题,使得系统架构的构建变得有章可循。
前面曾经提到过,Commonality/Variability分析方法可以有效的发现反映问题领域本质的概念,但是不能十分有效的发现抽象概念间的关系。如果Commonality/Variability分析方法和模式结合起来,就能够有效的解决这个问题。上一小节也说过,模式其实就是一些关系。如果我们有一些需求,并且我们知道有针对该需求的模式,那么我们就可以通过Commonality/Variability分析方法发现反映问题领域本质的概念,然后根据可用的模式去建立这些概念之间的关系来实现我们的需求。这样我们就可以通过模式有效的克服不能有效确定概念间关系的难点。
模式本身并不仅仅简单是一些关系,它还具有深刻的内涵。它具有自身的意图、动机、适用性以及核心解决方案等众多的内容。一旦我们确定使用了一个模式,那么这个模式就为我们搭建了一个内容丰富的场景,这个场景可以非常有效的指导我们进一步的工作,启发我们发现新的对象。这样,基于模式构建的系统架构就变得抽象(模式本身就是一些抽象的关系)而不空泛并且具有很好的延伸性。
模式也提供了一套公认非常有效的记录方法,可以把自己经过反复验证的解决方案记录下来传授给其他人重用,即模式具有可传授性。这样,有经验的系统架构师就可以把自己的一些心得体会通过模式的方法记录下来,就可以克服经验无法有效传授的问题。
其实很多模式本身就是针对系统架构提出的,比如:MVC(Model-View-Controller),它是专门针对交互系统提出的,所以如果我们要构建一个交互系统,那么我们就可以直接应用MVC模式,然后在该模式所搭建的场景的启发下去发现Model、View以及Controller,在这个大的场景的指导下根据其它的需求(模式)构建一些小的场景对系统进行有效的分化。细心的读者会发现,模式和系统架构有很大的相似性,都是处理一些抽象概念间的关系,但是二者还是有很大的不同的,模式是领域无关的,它是解决一些抽象问题的,但是系统架构是针对我们要解决的实际问题的,是领域相关的。我们可以通过对问题领域的分析、分解,找到和我们要解决的问题匹配的模式,对该模式进行定制应用到我们的系统中来。把模式结合在一起构建起整个系统架构来。
面向模式构建的系统架构在需求发生变化时还会使我们处于一个非常有利的位置。为什么呢?因为模式是针对一个反复出现的问题的优秀的解决方案,它的方法就是发现变化、封装变化。它本身已经充分考虑了变化的情况。模式采用了一种不同的对待变化的方法,它不是预先考虑会如何变化,而是考虑哪里可能会变化,然后隔离,所以当变化发生时我们就处于一个有利的位置上。
最后让我们来看看模式论坛研究、探讨的一个焦点论题:模式的生成性。模式的生成能力是指模式创建最终行为的能力。有人认为,模式系统可以形成一种语言,即模式语言。模式既是模式语言的要素,同时又是模式语言的规则。象数学证明一样,可以通过模式的推导、演绎生成系统架构。当然,目前的模式语言远远还不完备,还不具有如此强大的生成能力。不过,至少为我们的软件开发的永恒解决方法提出了一个美好的希望,让我们大家共同来为此努力。有兴趣的读者可以到hillside.net获得更多有关模式语言的信息。
JUnit架构一览
前面讲了这么多,本小节我们将结合一个实例进行说明。我所选择的是JUnit,一个很著名的单元测试工具,选择它作为例子主要有两个原因:其一是JUnit相对比较简单,这样我可以集中于要论述的主题;其二是JUnit中大量使用了模式,这样大家可以对于上面的论述有一个感性的认识。下面对于JUnit的说明会比较粗略,读者可以到www.junit.org去了解有关JUnit的更加详细的信息。
JUnit是一个单元测试工具,所以它就要满足使用者对于单元测试工具的需求。让我们一一来进行说明,首先测试用例的书写必须要容易,只有这样大家才可能会使用该测试工具。要迎合这一点,我们可以通过把测试用例表示为对象的方式来完成,对象的概念和人思维中的概念最为贴近,表示起来也最为自然。Command非常符合我们的要求,看看它的意图:将一个请求封装成一个对象,从而对请求进行排队或者记录日志…。另外,测试工具要为使用者做它能够做的最多的事情,尽量减少使用者的工作量,我们必须对测试的一般流程进行分析,归纳出一个模板流程,这样就可以使使用者仅仅关注自己所需要的具体步骤而不用考虑如何去组织这些步骤(一般的测试都分为测试准备即setUp,运行测试即runTest以及结束清除即tearDown阶段),Template Method模式正好能够帮上忙。另外,测试工具应该能够统一处理单独的用例以及多个用例的组合,这样也可以大大减少使用者的工作量,是不是马上就想到了Composite模式,不错,就是它。
下面根据上面的论述,在我们所得出的模式场景的指导下,给出JUnit系统架构的UML图表示:
Junit本身提供了源代码,有兴趣的读者朋友可以参阅。不过如果了解了系统的架构后在去看源代码的话,相信会有不同的感受。
结论
本文从宏观上探讨了系统架构、模式以及模式在构建系统架构方面的的有效性。限于篇幅,其中有很多重要的内容没有做十分深入的论述,关于这些内容准备在后续的文章中作为独立的主题来讲解。希望本文能为读者朋友带来一些启发。最后我就以模式的提出者Christopher Alexander的一段具有深刻内涵的话作为结束:"以一种松散的方式把一些模式串接起来建造建筑是可能的。这样的建筑仅仅是一些模式的堆砌,而不紧凑,这不够深刻。然而另有一种组合模式的方式,许多模式重叠在一个物理空间里,这样的建筑非常紧凑,在一小块空间里集成了许多的内涵,由于这种紧凑,它变得深刻。"
参考文献
The Timeless Way of Building, Christopher Alexander
Design Patterns Explained: A New Perspective on Object-Oriented Design, Alan Shalloway and James R. Trott
Multi-Paradigm Design for C++, James O. Coplien
Design Patterns, Gamma, et. al.,