2009年4月29日星期三

MVC模式初步

2.4节首先提到了interactive系统,这类系统使用图形化的UI来完成高度的交互,使用户能够方便地访问系统的服务。构建这类系统时,一个难点就是:将UI和系统的核心功能分离,因为系统的核心功能一般是保持稳定的,而系统的UI却时时变化,或者很可能有多套的UI。为此,对于这类 interactive系统,书中介绍两种模式:Model View Control模式和Presentation Abstraction control模式。

关于MVC模式的资料和文章很多,现在发现不同的文章里对MVC的介绍和解释各有不同,且按下不表吧,现在POSA 里是怎么说的。Model类,其职责是提供应用的核心功能,为独立的View和Control提供注册(对于这一点我以前是不知道,或者不明晰的,View和Control要在Model里面注册,我认为这么做的目的当然是为了让Model能够了解掌控View和Control的信息,以使得 Model能够将更新通知给两者,这里很自然地想到了Publish-Subscribe模式或者observer模式),当数据更新是通知注册的 View和control。View类,其职责是:创建和初始化相关的Control(Control和View联系较紧,因为不同的View有不同的输入处理方式,所以Control由View对象来创建。在书中的MVC实现步骤中还提到,虽然一个View只对应一个Control,但是对应的 Control是可以动态的变更的,例如在运行时,动态地更换View对应的Control,使得View对用户的输入作出不同的响应。),显示更新信息,从Model处获取数据。Control类,其职责是:接收用户输入,将事件转化为服务请求并发送给Model。

图一是MVC的类图

图二是GoF的observer模式的类图,可以比较一下,两个图是非常相似的。

图三是MVC模式各个类对象的初始化序列图,需要关注的是各个对象的创建过程。

2009年4月27日星期一

Broker模式

Broker模式用于分布式系统,对系统中的各个组件解耦,协作,通信等。服务在Broker中注册自己,使得client可以通过它们提供的接口访问自己,client向Broker发送请求以访问服务提供的功能,Broker根据请求定位合适的服务器,然后将请求发送给它,并将结果和异常等信息返回。

我的理解,这个Broker模式就好比生意场中的代理人或者中间人,他负责联系买卖双方,通风报信,促成生意。也可以这么理解,Broker是一个中间层,它封装了各个服务的实现细节,client不必(估计也不想)知道服务的细节,只要给Broker发送请求,剩下的事情由它去办,这就使得client和server的耦合很松,整个系统的扩展性有了大的提高。

Broker模式包括六种主要的组件:clients,servers,brokers,bridges,client-side proxies和server-side proxies。

server实现某些功能并通过接口向外提供之。server的责任包括:实现服务,在本地的broker中注册自己,通过server端的proxy向client发送响应和异常信息。server的协作者包括server端的proxy和broker。

client的责任包括:实现用户的功能,通过客户端的proxy向server发送请求。它的协作者包括客户端的proxy和broker。

在文中用messenger描述broker,它是client和server之间通信的桥梁,它将client的请求发送给server,也将server的回应和异常发送给client。因此,broker必须通过某种方式实现唯一的标识,来分辨和定位数据的来源和目的地。它提供接口使得client和server都能在它那儿注册。其实这很好理解,因为只要这样,broker才能统领全局,了解所有已经存在的client和server。前段时间看了Android,现在想起来,它很可能也使用的这个模式,Android工程里的AndroidManifest.xml的功能应该就是这样,所有在工程添加的类,无论是Activity,service,还是broadcast receiver等等,都必须在AndroidManifest.xml中注册,否则应用不能实现相关的功能。我推测Android里应该专门有一个模块来管理AndroidManifest.xml,每当一个应用被按照进系统后,这个模块就检查AndroidManifest.xml,收集信息,将里面的类,服务,过滤器等等信息都收集后保存,这样当有Intent(特别的隐式的Intent)被抛出后,系统才能够定位相应的Activity或者service,否则系统是找不到的。

client端的proxy是client和broker之间的中间层,它封装系统指定的功能,例如client和broker之间的具体通信机制,内存块的分配和删除,参数和结果的排列等,可以这么说,client的proxy是一个打包器,将client的请求进行封包。而与之相对的是server端的proxy,就是一个拆包器了,同时将server的结果和异常信息打包发给client。

Bridge是一个可选组件,在Broker系统里,很可能是一个异构的系统,存在多个Broker,各个Broker之间如果要通信,就需要使用Bridge,它在各个Broker之间找出路由。

Broker模式分为两类:一类是没有broker,client端的proxy和server端的proxy之间直接通信,不用broker来中转通信,这类broker模式的好处是性能高,因为省去了一道中转,但是在两端的proxy之间需要使用同样的数据格式或者协议来传输。另一类就是有broker,两端的proxy使用不同协议或者数据格式不要紧,由broker来统一。

2009年4月22日星期三

黑板模式及其实现

对于Blackboard模式,看完前面的介绍还是有点迷糊,书中一开始就提到了几个现实问题,例如图像,语音识别,这类问题当前虽有多种解决方案,但是 这些方案并不是非常成熟,也没有非常好的算法来实现。书中解释了为什么用人工智能和专家系统来解决这类问题的局限性,不过这段没怎么看懂。

黑板模式的思想是,有一系列独立的模块,或者说是方案,这些方案能解决部分问题的一部分,这些方案进行协作,使得问题问题能够最终解决。这就像一群人在一块 黑板前,共同解决一个问题,根据当前问题解决的程度和状态,不同的人上前到黑板上解决他所能解决的部分,这样经过多人的协作,最终能够将问题解决。这就是 黑板模式这个名字的来历。这里有个问题,就是根据当前的解决状态,下一个由谁上前去解决,是由个人决定的,因为个人知道该谁上前去,那么在软件中,前面提 到了,有多个方案,这些方案能解决问题的一部分,就相当于单个的人,那么当前由哪个方案去解决,由谁来调度,书中提出,黑板模式中有一个仲裁组件 (moderator component),它根据当前的状态来调度有哪个模块去解决。

关于黑板模式的实现,书中提到,将黑板模式的实 现分为三个主要的组件:Blackboard,Knowledge Source和Control。Blackboard可以看作一个容器,它存储数据,包括控制数据,状态数据等等。Blackboard为这些数据提供了 读写接口使得Knowledge Source可以访问它们;Knowledge Source的职责是评估自身的能力以及适合自身执行的环境和前提条件,因此Knowledge Source也可以分为两个小部分,一个是condition part,用于评估当前的状态,通俗的说就是对于某个问题,当前解决了多少,得到了什么中间结果,当前需要解决什么问题,这些条件是不是满足自身的触发条 件以使得自身可以运行,另一个是action part,用于执行相应的操作,得到相应的结果,同时也要保证Blackboard更新最新的结果;Control的职责是监视Blackboard的变 化,决定下一步的动作,它调度Knowledge Source评估当前的状态以决定下一步动作,这里有个策略的问题,可能遇到两种困难的情况:一种是根据当前的状态,找不到一个匹配的Knowledge Source来处理;另一种情况是当前的状态可以有多个Knowledge Source匹配。这里需要有一个策略以处理上两种情况。

最后要注意的一个问题是何时停止的问题,也就是出口,因为书中提到Control是一个loop,不停地查询Blackboard的数据和状态以调度,如果 这样下去就是一个死循环了,需要一个出口,得到什么结果时停止。这也是有Control控制的。下图说明三个组件的关系:

2009年4月21日星期二

Pipes and Filters pattern的概念和实现

对于the Pipes and Filters pattern,书中提到了几个概念:管道(Pipe),过滤器(Filter),数据源(Data source),数据渊(Data sink)。管道用于传输,缓冲和同步数据,它可以与数据渊,数据渊,过滤器协作;过滤器用于获得输入数据,执行相应的操作,输出数据,它与管道进行协作;数据源用于将待处理的输入数据传给管道,它与管道协作;数据渊用于收集输出数据,它与管道协作。

另一个概念就是主动和被动,管道,数据源,数据渊都有主动和被动之分,两者的区别,一个是主动的去获取或者输出数据(书中的pull和push),而被动的则是通过外界触发,例如事件,或者相应的协作对象调用来触发。

Pipes and Filters 的实现:
1,将系统任务分为一个顺序的执行状态。没个状态只依赖他的直接前驱的输出,也就是说这些执行状态是一个线性的结构,第n+1个状态只依赖于第n个状态的输出结果(直接前驱),而不是还依赖与其它的前驱。
2,定义在管道中传输的数据的格式。为数据定义一个统一的格式可以有效地提高可扩展性,因为这样一来,就可以通过组合不同的过滤器以实现不同的功能,因为数据的格式是统一的,所以各个过滤器的前后次序不必担心,如果数据的格式不统一,那么调整过滤器的顺序就要大费周章,因为过滤器输入输出的数据格式不一样,调整过滤器的顺序需要进行大量的修改。
3,决定如何实现管道的连接。可以通过直接调用的方式来实现管道的连接,例如前驱的过滤器直接调用后续的过滤器中的方法,这种管道的实现方式可以实现数据的传输,但是这种方式的缺点是不言而喻的,就是耦合性太强了,一旦需要调整过滤器的位置,那么大规模的修改是不可避免的。比较好的方式是利用操作系统提供的相关特性来实现管道,例如可以用Windows下的message queue等用于进程间通信的方式,在Linux下用管道来实现。
4,设计和实现过滤器。过滤器的实现要考虑前面提到的主动和被动方式,对于被动型的过滤器,可以用一个或者一组函数来实现即可;而对于主动型过滤器,应该考虑用线程或者进程来实现。
5,设计错误处理。

2009年4月20日星期一

层模式的实现及管道过滤器模式初步

层模式的实现步骤:
1,定义分层标准,也就是需要一个准则,用它能把待开发的系统抽象出各个层来。这个准则很重要, 因为它觉得了层的划分和粒度。书中提到一个大概的准则,从上到下依次:User visible elements;Specific application modules;Common services level;Operating system interface level;Operating system 和 Hardware。
2,为各层命名并分配任务。
3,为各层指定服务(我这里疑惑的 是服务和第二点的任务有什么区别?)。书中提到最重要的实现原则是各层要严格地隔离开,杜绝跨层的组件。某个层中的函数和接口,它们的返回值,参数,错误 类型等等,都应该是某种语言的内建类型(例如int,float等)或者是在改层中定义的类型,或者是一个公共的数据类型。但是书中的意思是,使用公共的 数据类型会影响各个层之间的界限和划分,所以应该要少用。
服务要多放在上层,下层要保持苗条,少放服务。上层使用下层的服务,下层为上层提供服务。
4,重新分层,这是一个迭代的过程,因为在初始的划分中难免有错误和不合理的地方,例如某一层会使用多个层的服务,而不是只使用它的下一层的服务,这是违反层模式原则的。
5,为每层指定接口,这个接口是为了上层能够使用该层的服务。对上层来说,下层最好是像一个黑盒,上层不知道下层的实现,但上层知道下层提供的服务并可以使用这些服务。
6,考虑每层的结构,传统上,很大部分的精力都放在如何在层与层间建立更好的关系,但是单个层内部的结构也很重要,如果某一层非常复杂,那么可以将该层分为多个组件,并对这些组件应用设计模式。
7, 指定层间的通信,书中提到推拉模型,第J层调用第J-1层的服务,将所需要的信息通过调用传给J-1层,是为推模型;低层将信息返回给高层,是为拉模型。 推拉模型可以用Publisher-Subscriber模式,或者Pipes and Filters 模式来实现。
8,对相邻的层解耦,前一步提到了指定层间的通信,用推拉模型,不过这可能会造成相邻两层耦合度比较高,书中提到可以用接口和回调函数降低耦合度(不过我还是不明白为什么用回调就能解偶,我觉得回调函数和Publisher-Subscriber模式没什么区别)。
9,建立错误处理机制。

层 模式的变体,可以称为松散的层模式,松散层模式中,高层不但可以使用下一层的服务,也可以使用其它低层的服务。这样的好处是效率和可塑性,但这是以牺牲可 维护性为代价的。一般在系统软件中使用这种松散的层模式比较多,例如Unix,X Window等,因为这些系统一般比较稳定,不需要经常做大的修改,它们对效率的追求也比较高。

the Pipes and Filters pattern:
为 说明该模式,书中举了一个例子,假设有一个新的语言,类似Java,要为该语言开发编译器,编译器的处理过程就是一个数据流的处理过程,首先输入的是 ASCII码的文本,也就是用该语言编写的源文件,经过扫描,得到句元流(Token stream),经过语法分析,得到抽象语法树(abstract syntax tree),经过语义分析,得到argument abstract syntax tree(不知道怎么翻译),再经过中间码生成,得到中间码(和Java的字节码一个意思),最后经过优化,得到可在
特定CPU上执行的机器码指令。如下 图所示:

2009年4月17日星期五

architectural pattern的分类

第二章主要讲述architectural pattern:“Architectural patterns express fundamental structural organization schemas for software systems. Architectural patterns represent the highest-level patterns in our pattern system. They help you to specify the fundamental structure of an application.”

这一章总共包括8个模式,这些模式又分为4类:
  • From Mud to Structure
我的理解,这类的模式主要的作用是结构化,避免大型的系统成为一盘散沙(mud和a 'sea' of components or objects),让系统条理清楚。这类的模式包括: Layers pattern,the Pipes
and Filters pattern,和Blackboard pattern

  • Distributed Systems
专门针对分布式的模式,包括一个模式Broker pattern,这类模式还和另外两个模式有关,Microkernel和Pipes and Filters。

  • Interactive Systems
用于交互系统,也就是内部交互比较多的系统吧,一般的UI和MMI系统都可以算作这类系统。包括两种模式Model-View-Controller pattern和Presentation-Abstraction-Control pattern。

  • Adaptable Systems
用于对扩展性要求很高的系统,随着技术的更新,需求的变化,系统需要不断地扩展。包括两个模式:Reflection pattern 和 the Microkernel pattern

先 从From Mud to Structure的几个模式谈起,Layer模式,是针对与那些可以被多个子功能块的系统,每个或者每组功能块可以被实现为一层,但是书中也提到了,对 于一个大型的软件系统来说,有时候有些功能块的界限并不明晰,不好区分。Layer模式是三种模式中使用最广泛的,很多大型的软件都是以层来架构的,特别 的网络方面的系统,基本上都是分层架构。

the Pipes and Filters pattern 主要针对出来流数据的系统,例如视频音频流等,每个处理步骤都被封装在一个过滤器对象中,流数据在各个过滤器中通过管道传输,组合不同的过滤器能得到不同 的结果。对这个模式,我以前不知道,当我读到这里时我就有些明白了,当年在听DirectDraw和DirectShow的presentation的时 候,对于流媒体的文件,MPEG4,Mp3等等,可以用相关的工具分析里面的过滤器及接口,如果想要自己做一个播放器,也要建立相关的过滤器和管道,想到 那次presentation的内容,再结合今天读到的这个模式,我就明白了,原来这也是一种架构模式啊,我估计很多媒体播放器就是用的这个架构。

至于Blackboard pattern,书中提到这是来源于人工智能,当你要开发的系统,它的需求并不明朗,位于一个全新的不成熟的领域,那么这个时候,可以用这个模式,先做一个次优的(suboptimal)方案,待到功能逐渐明晰和成熟后,再来修改。

2009年4月14日星期二

对三种模式的理解

昨天提到了在POSA中把模式分为三种,今天继续看,写一点对三种模式的理解。

architectural pattern:An architectural pattern expresses a fundamental structural organi-zation schema for software systems. It provides a set of predefined subsystems, pecifies their responsibilities, and includes rules and guidelines for organizing the relationships between them.
我的理解,architectural pattern为整个软件系统奠定了整体的框架和架构基础,软件系统的框架搭建好后,详细的实现就在这个框架下进行,比如整个软件系统分为几个模块,或者 说几个子系统,每个模块的责任是什么,各个模块之间的关系是什么,把这些厘清后,具体的细节就好实现了。MVC就可以看作是一种 architectural pattern。

design pattern:A design pattern provides a scheme for refining the subsystems or components of a software system, or the relationships between them. It describes a commonly-recumng structure of communicating components that solves a general design problem within a particular context.
我的理解,design pattern比architectural pattern要轻量级,POSA说“Design patterns are medium-scale patterns“,它主要用于模块或者子系统。

idioms:An idiom is a low-level pattern specific to a programming language. An idiom describes how to implement particular aspects of components or the relationships between them using the features of the given language.
我 的理解,这个idioms还不知道怎么翻译,按文中的意思,它是和特定的语音相关的,用特定的语言实现模块的部分功能或者模块间的关联。从这里看出,它比 design pattern又低了一级,它实现更细节的东西。书中举了个例子,就是引用计数,C++用引用计数来管理动态分配的资源,而smalltakl提供了 gc,所以不需要用这种方法。目前对这个idioms还没什么概念,在后面的学习中再领悟吧。

高级别的模式很可能要搭配使用低级别的模 式,书中举了一个例子,就是MVC,它是一个architectural模式,View是处理显示的,Control是处理输入的,Model是做业务处 理的,前两者和后者紧密联系,当业务处理有了新的结果,那么这个结果必须马上被显示处理,必须立即将新的结果通知View,当有用户输入发生时,也必须立 即通知Model进行业务处理。这种情况下,就可以使用design pattern里面的Obeserver模式,定好三者之间的subject和obeserver角色,就可以很好地解决这个问题。各个不同级别的模式经 常要混合使用以达到好的效果,所以Alexander说:Each pattern depends on the smaller patterns it contains and on the larger patterns in which it is contained.

2009年4月13日星期一

POSA的第一篇日记,何为模式

用processing,outpu和input来解释MVC很有点意思,processing,output,input分别对应Model,View和control。

模式可以从三个方面来考虑:context(场景或者背景,也可以理解为已知的先决条件);problem(可重现的问题,由前面的场景引发,需要解决什 么,要达到什么目的);solution(方案,一个已被用过的,或者被论证过的有效方案,解决特定场景的某类问题的方案,这个方案可能只是一个框架,这 个框架是成熟的,你可以对这个成熟的框架进行修改定制以更好地解决你的问题)。

POSA里将模式分为:architectural pattern,design pattern和idioms,这个GOF的分法有点区别,GOF分为creational,structural和behavioral。这个在以后的学习中慢慢体会吧。