« Science | 主索引 | 归档 | Technology »
Mach de Bagua: Software的归档

Software分类的最新日记

从我最熟悉的设计模式写起。英文是chain of responsbility,常常被译为责任链模式。

在1980年代,报纸上常见的一个批评政府的词汇叫"踢皮球"。当下看来,从社会主义社会转型到资本主义社会,这当中有诸多的未定义案例出现,导致了政府部门互相推诿的现象。在软件设计中,有时候也会有类似的案例,就是不知道一个事件/数据该由哪个对象来处理,或者是需要多个对象来依次处理,就出现了责任链模式。

责任链与踢皮球的不同之出在于,责任链不可以把球回传。责任链应该是一个单向可走通的链,允许中间退出,而不允许往回走导致死循环。

前面说过,设计模式的思想不限于面向对象设计。。。那么先举一个PC机上PCI总线的中断共享的例子。我还记忆犹新,当年上大学的时候,用着导师的一款386机器,很不错的,装了ISA网卡,在DOS下上网。后来想在那台机器上玩大富翁3, 找了个ISA声卡,费了九牛二虎之力才装上啊,记得是把网卡声卡的中断全改了,还改了DOS的config文件。。。

为什么装个声卡会这么痛苦?原因么,当然是因为ISA总线是不支持PnP的,共享内存地址和IO口地址都要手工指定,中断也不可以冲突。。。其它不深入了,总之后来的PCI总线就支持中断共享了。按照一般人的想法,一个PCI系统,当中断发生时,系统应该可以通过中断号、中断发生的PCI槽号来找到对应的驱动程序(别忘了多块PCI卡可以共享一个中断号),调用中断回调函数。--可是,令人发指的是,由于历史原因,主要是中断控制器的设计的一些兼容性原因(PCI和ISA是要共存的),当中断发生时系统没有办法知道是哪个槽中的PCI卡发出了中断,系统只能得到一个中断号。。。

那么我们来看一下WindowsNT下的驱动程序是怎么处理的(Linux下其实也类似)。首先,计算机开机。上电的时候系统能够识别那个插槽插了什么卡,得到它的厂商ID和板卡型号ID,并写入注册表。系统也会得到它所期望的资源类型和大小,并分配一块物理资源。Windows会根据厂商ID和板卡ID, 去寻找已经在注册表里注册的驱动程序,并使用驱动里的AddDevice功能增加一个实例。这个AddDevice中间包括了往PCI卡的配置空间写资源和向Windows注册资源的,包括注册中断号和中断回调函数的过程。这一点Windows做得很绕,先是分一块资源,把这些资源告诉PCI驱动,然后驱动再自己去注册这些资源。到了这一步,Windows内存里就有了以下模块(假设有三个板卡使用中断号17, 顺便说,很多主板都不完整支持PCI规范,也不支持中断号16~31),结构图是这样的

当中断发生时,通信图是这样的。这里画了一个最糟糕情况,当a收到中断请求包(IRP)时,它去查自己的板卡的寄存器或者共享空间,判断是否自己发出了中断,如果是自己的,立即擦除板卡上的标志,并且改变IRP的状态。题外话,因为中断是不可重入的,在这里做太多事情会影响多线程系统的可协作性,那些响应中断的活应该调用驱动里的DPC(推迟过程调用)来实现。中断服务程序应该在改变IRP状态字后立即返回。Windows核心会判断IRP的状态字,决定是否需要把这个IRP传递给下一个中断服务函数。所以责任链并不一定要调到到链上的所有元素,它是很有可能中间就完成任务。


如果觉得上面的例子太过晦涩,请看第二个例子。最近我需要做一些文件的文字处理。我有一个C++写的处理程序,叫Process。可耻的是,Process的功能不够强,有一些特殊的情况处理不了,所以我用一个perl脚本pre-processor.pl去预处理一下,把输入文件规整成可以被Proess的格式。再可耻的是,Process的处理功能还是不够强,出来的文件某些地方不符合我的需求,于是我再写了一个脚本叫post-processor.pl的来后处理一下。最后,我不愿意手工做这些事情:去调用、判断中间结果是否生成,另外,我还需要输入一个目录下的所有文件逐个处理,所以我写了一个shell脚本来做这个事情。

怎么样,我把文件画成了圆圈,很生动的踢球过程吧。上图只画了最佳结果,没考虑处理失败的情况,程序需要处理的。


最后,作为面向对象的责任链模式,有什么特征呢?很容易想到,所有的责任处理模块都有相同的输入和输出,所以这些模块作为类,应该有共同的基类。这是四人帮的书里举的例子,里面有一个Handler基类和两个派生类


这个例子,让责任处理类互相知道彼此的存在,一个责任处理类处理过输入之后,再传递给successor里所存的下一个责任处理类实例。这样做的一个坏处是,责任处理类必须知道谁是它的下一个负责者(successor),而且也很难在运行期改变这个链的结构。

我个人更喜欢有一个额外的中介类来存储责任链链表,这个类里面有一个链表存储所有的handler, 有一个接口接受client输入,有方法响应外部从链表里增加或删除Handler。这样具有更多的灵活性,比如可以方便地修改责任链;甚至是取消一个已经进入责任链的请求。更复杂一点的来说,当handler的处理速度有可能瞬时慢于client的请求速度时,可能还需要实现一个buffer来存放请求(下图没有画出这种情况,而是假设client自己处理了这种情况)。


有没有觉得上图我的做法(不考虑责任链的功能)也许可以归纳出另外一种模式?没错,看四人帮对Mediator( 中介者)设计模式的定义:"用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。"--所以上图是中介者模式和责任链模式的一个混合。


总结:(1)当有一个请求需要处理,但发送方不知道应该由谁处理的情况,可以使用责任链模式。责任链降低了请求发送类和多个处理类之间的耦合度。
(2) 当你有很多类来协同处理一个事情时,考虑用一个中介者来掩盖各对象之间的耦合。

设计模式(00)

| | 评论(0) | 引用通告(0)

翻箱底,几篇很久以前没写完的笔记。。。

看过金庸的武侠小说《笑傲江湖》么?里面有个华山派,华山派有剑宗和气宗之争。剑宗讲究的是招式,而气宗推崇内功。软件的世界,也是一个江湖,行走江湖就是不断出手的过程。很有趣的是,武术的招式和内功在软件的位面里有着投影。。。

什么是软件的内功?对编程语言的理解、对算法/业务流程的理解可以被看作内功。例如说,我常被新工程师问到的一个C的问题是:请教把"2046"这个字符串数组转换成整数应该怎么办?--好吧我的答案就是一句话,"函数atoi(),自己去查MSDN"。知道不知道一个功能函数,无关个人能力,只是他有没有遇到过的问题,更多的是经验的积累加上一点本能,很符合内功的一些特点:需要通过修炼(做项目)获得,通常年龄越大内功越高。近年来开始有人试图在软件设计方面归纳出一些内功心法,就是所谓的DDD(Domain Driven Design),这里面的domain指的是业务领域,对它的建模叫领域模型。这个话题以后再谈。

再说招式。早先学计算机的人都知道一个等式,"算法+数据结构=程序"。这个等式在早期程序大多开发用来坐科学计算的时候是适用的,但是,一个软件,除了算法和数据结构,就没有其它东西了么?那个加号是什么?其实是值得琢磨的东西。而且这一点在面向对象的时代显得更突出,就是对象之间的关系用什么来描述?即使在非对象的编程里面,一样也有模块的概念,最小粒度的软件模块之间关系同样是值得关注的话题。

1995年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides这四个人(常被称为四人帮,在英文里就是GoF)写了一本著名的书《Design Patterns: Elements of Reusable Software》,直接了当地说,软件对象之间的关系可以用建筑学的名词design pattern(设计模式)来概括,而且列出了23种常见设计模式。这几个人厉害就厉害在,后来其它人想了又想,却不太能想出更多的设计模式,就是说天下武功都不出这23招,洞察力果然了得。

值得指出的是,说到四人帮的模式的时候,一定要指名带姓说"设计模式",光说模式是不精确的,因为设计模式其实是软件设计里最低的一层,再低就低到软件实现里去了。在设计模式之上,还有所谓的"软件模式",例如著名的MVC模型,同样不是在这里讨论的话题。

往往会有这样的情况,一个工程师可能用过某个设计模式,而其实他并没有看过四人帮的书。这很好理解,设计模式本来就是软件实践的概括。但一个工程师23个设计模式都用过的,倒也不多,从设计模式的书里面往往可以得到一些启发。这就是学习设计模式的意义了:总结历史,学习经验。

下面会随机挑一些设计模式来做分析。有两点要说明:
1) 我认为设计模式的实践不限于面向对象设计(OOD),所以有可能举一些非面向对象的例子。
2) 为了说明类或者软件模块之间的关系,使用了UML标识图。UML是一种用来标识对象和对象间关系的图释法,相当直观,看懂它不比看懂通用地图的图释难多少。用UML结合Rational公司的软件Rose来做设计并自动生成代码当然要仔细了解使用细节,但只是为了看懂UML图而去看那些砖头一样厚的书是浪费时间。UML仅仅是工具而已。

在这里列出四人帮归纳的23种模式,他们把它们按目的分成了三个组,按作用对象分了两个组,结果就是这样的一个矩阵。其中Adapter模式即可以作用于类,也可以作用于对象,所以矩阵里面有24个项目。

创建型 结构型 行为型
Factory Method Adapter Interpreter
Template Method
对象 Abstract Factory
Builder
Prototype
Singleton
Adapter
Bridge
Composite
Decorator
Facade
Flyweight
Proxy
Chain of Responsibility
Command
Iterator
Mediator
Memento
Observer
State
Strategy
Visitor

在继续深入之前,强调一下,设计模式是软件设计中的重要一环,但是领域模型也很重要,很多时候都要比设计模式更难做好,相比之下设计模式要简单一点。。。个人观点。这么说我是华山气宗的观点了,嘿嘿,要和君子剑岳不群岳先生划清界限啊。。。

未完待续。

关于此归档

这里是Software分类日记的归档。

上一个分类Science

下一个分类Technology

主索引归档页可以看到最新的日记和所有日记。

Powered by Movable Type 4.0