作者: 点空间 陈国生 首先,我们从一个设计模式说起:Facade 外观模式。我们来谈谈这个模式,这个模式的用途是将一个子系统内部的众多对象统一起来,产生新的接口出来。这让我想到我们中国有句成语叫:老干新枝。这个意义是,老干已经失去了活力,但为了生存下去,必须产生新的枝芽,这跟一个已经僵化的系统一样,我们无法期望从原来的枝长出叶子,所以只好另长新芽。 如果一个系统已经纠成一团,如左图般的状态,再继续扩充功能,或更动系统功能,那无疑的是雪上加霜,加速系统的走向死亡之路。由于系统内部的对象交互影响,只要移动部分的功能,Client 端的使用接口势必要跟着改变,继续使用这样的系统结构,终究有一天会土崩瓦解。重新去调整这个致命的缺点,又跟重新改写没有两样,常常这样让人陷入进退两难的窘境。 如果我们可以用设计模式去思考解决问题,如右图将原系统视为一个子系统,使用Facade这个样式,重新产生使用接口,将这混乱的局面控制,不再像癌症一样继续扩散开来,内部的调整不再影响Client 的使用接口,那么就可以让系统取得一个扩充功能的契机。系统控充功能与内部结构的改善,这是两个不同的方向,千万不要搅在一起,也不要同时去改变,这样会使问题更加的复杂,
俗语说:做衣容易,改衣难。这说明一件事,虽然做衣服跟改衣服,用的是同样的技术,但却是不同的思维,所以变成了会做衣服却不一定会改衣服。对设计系统而言,系统中任何一个变动,就像改衣服一样,学习克服这些变动,才能快速的反应变动。近年来软件工程的思维倾向贴近客户需求,拥抱改变,但这不光是口号而已,在这个思潮下,背后必须有强而有力的设计技术及思考模式所支撑,因此学习这些基本技术及不断的磨练你的思考模式,才能去符合这样的要求。本篇就是基于这样的想法,将设计模式与重整两个重要的基本技术加以结合运用,实现系统面对改变时,如何去反应变动,及如何让系统保持适当的弹性。 底下,对本篇所提供的实作范例,作简单扼要的说明: 范例一
运用Observer 观察者模式解开单元之间的耦合
作者:陈国生 【解决问题】 在Delphi 的对象中,TForm 是一个很抢眼的角色,因为在一个Form 当中,我们可以轻易的结合许多不同的对象一起工作,几乎是无所不能,但是在比较复杂的系统中,我们必须根据不同的功能,切割放在不同的Form 上面 ,或者说是在不同单元当中。当一个系统存在许多不同单元之后,我们要如何去让这些单元可以相互合作?使用USES 可以将两个不同单元视为一个单元来使用,但遗憾的是,这种设计方式往往会造成单元之间的耦合性过高,当一个单元变动之后,往往其它的单元也会跟着受影响,而整个系统也就因此牵一发而动全身了。
【运用技术】 本范例将运用Observer 观察者样式,来解决单元之间的纠缠不清。首先我们先来看看这个观察样式定义:观察者模式定义对象间的一对多的相互关系,使得一个对象改变时,其它相关对象皆可获得通知并自动更新。这个模式里面,会有一个观察者,及一个以上的被观察者,当观察者的状态改变时,被观察者被主动的告之,而观察者负责通知,被观察者却不用知道观察者是谁。 从理解一个抽象概念的,到运用在解决问题上面,过程通常不是那么的容易。根据观察的样式提供的概念,其实Delphi 这个RAD 已经帮我们设计许多功能强大的对象,所以重要的是,我们如何去运用这个概念去解决问题,要把焦点放在解决问题上面。 观察者模式,有点类似Window里面的讯息传递机制,如果我们要实作这样的机制,那么必须要有一个可以储存这些讯息及事件的容器型对象。在Delphi 中有许多现成的对象,你无须费心的去设计这样的对象,只要找出可以实践这样想法的对象即可,本范例中,我们运用TActionList来实现,TActionList 是一个事件串行,现在就看我们如何善用这个对象,去达到讯息传递的目的了。 在下面的范例中,我们将实作一个横跨整个系统的观察者样式,解开单元之间的耦合问题。 【实作范例】 第五步,在Form2的Edit1的OnKeyPress 写入: 从这个范例中,可以将Form2视为是一个观察者,而Form1 是一个被观察者,当Form2的TEdit的值改变后,透过事件通知,改变了Form1的TEdit,然而Form2 并无USES Form1 ,也不知道Form1的存在,所以即使Form1消失了,仍然不会影响Form2的运作。 【结 论】 在一个大的系统里面,控制单元之间的耦合性非常重要,以上述的范例来说,不论系统如何改变,各个单元都可以维持正常的运作,既相互合作又不会相互影响,其变动姓被有效的控制在一个事件串行当中,当单元变动时,我们很容易追踪,且不会扩散到整个系统。
运用Template Method 样式设计数据库的异动交易控制
作者:陈国生
【解决问题】 什么是异动交易控制?简单的说,就是如何确保在资料改变的过程,可以顺利的完成,异动交易控制最主要的着眼点在于对交易失败的控制,试想一个交易,基于某种不可预期的原因造成程序产生错误,中途被迫终止交易,那之前已经改变的资料算不算数?如果不算数,那之前已经交易的资料怎么办呢? 举例来说,一张进货单可能要同时更新单据档及库存档,如果中途失败,可能造成单据文件有资料而库存文件没有更新,要如何才能避免这种严重的错误呢?那就是必须进行异动交易的控制,在一个异动交易控制中,只要有一笔交易失败,那么之前所有交易的资料,将被还原至交易之前。 在Delphi 的Connection 对象已经提供了良好的控制程序。以TAdoConnection为例,它的语法格式如下: try
【面临的问题】 虽然Delphi 已经有了很好的解决方式,但是对一些已经写好的交易程序,我们在不变动原有的程序代码,如何进行功能的扩充呢?
【使用技术】 这个案例,我们将利用到重整技术的概念及样式设计来解决这个问题。范例中我们会用到Template Method 这个样式来设计一个程序,什么叫Template Method ?它的定义如下: 在父类别中定义一个算法的骨架,但将一些步骤延迟到其子类别中执行。套句话说,一个人犯罪是要坐牢的,这个犯罪要坐牢就是一个样版,至于做几年的牢,要看当时犯了什么罪才来决定。 这样的说法还是很抽象,我们进一步说明这个抽象的概念的运用时机: 利用Template Method 可以定义一些共享的算法,差异性部分则由子类别去实作,让一些有差异性的子类别,拥有共同的算法,如此则程序不但有弹性,且易于维护。 我们应该可以想象,异动交易控制就是一个算法,这个算法会被利用在许多需要控制异动交易的地方,每个异动交易的内容是不一样的,但异动控制的运算却是一致的。Template Method 这个样式的重点乃在区分变与不变的地方,将不变的部分写成骨架,将变的部分保留起来,对于一个异动交易控制程序来说,异动交易内容是变动的部分,而异动控的运算是不变的部分。 【实作范例】 接下来,我们来看一下,如何运用这个样式来解决问提呢,假设你有一个异动程序是这样: // 异动程序 现在,我们要为这个异动程序增加异动控能力,只要增加这样的一个程序就可以了: procedure TForm1.Button1Click(Sender: TObject);
这个范例运用Template Method 样式,避免修改已经完成的程序,同时又可以增加系统的功能。当然这个范例,也可以运用类别继承的方式来实现,其写作的方式,与上述的范例,亦相去无几,原事件改成宣告一个虚拟的程序,并在子类别中去实作这个程序即可,运用的概念是一样的,这正是所谓戏法人人会变,巧妙各有不同,融会贯通之后就可以运用自如了。
范例三
运用Factory 工厂模式设计多人使用的权限控制
作者:陈国生 在一个多人使用的系统中,根据不同的使用者,必须设计出不同的使用权限,要设计这样的权限控管机制,很显然相当复杂而且繁琐。因此,如何使用一个简单又容易维护的方式进行系统设计?在软件设计领域里面,简单就是美,这是在灵活塑模里面的一个宣言,追求简单,其实不简单,同样的一个问题,有千百种解法,如何使用一个简单又有弹性的设计,是程序设计者追求的极致,本范例的重点就是要具体实践这个目标。
【面临问题】 对已经写好的程序,我们不希望修改,因此如何以扩充功能的方法,将此权限控制的功能加入系统?
在重整技术中,有一个重要的原则,就是不要去更动已经设计好的程序,因此我们将设计一个即插即用的单元,为系统扩充多人使用的控管机制,因此在这个构想下,我们运用Factory 工厂模式的设计样式来实现这个设计。 现在,我们假设每一个使用者登入时,会对应到一个权限对象,然后针对这个对象去设计使用权限即可。换句话说,也就是说让不同的使用者对应到不同的对象上面,再运用工厂模式,根据不同的使用者,自动产生不同的对象,如此便可达到权限控制的目的。 当然,控制使用的权限,依控管方式不同,会方法有很多种,下面范例将提供其中一种控管方法。
【范例实作】 至此,我们已经把准备对应到使用者的权限对象TMainMenu 都设计完成了,接下来只要根据不同的使用者输出不同对象即可,这部分要如何设计呢?首先我在TDataNodule 的OnCreate事件中,加入判断使用者的功能,并输出对象: procedure TDataModule1.DataModuleCreate(Sender: TObject); var // 接下来输出对象至主画面 以上完成之后,最后的一个步骤是把TDataModule 在项目中自动建构起来,由于设计上是自动输出对象至MainForm,所以TDataModule 的建构必须在MainForm 之后,这样就算大功告成了。注意:本范例无处理User 登录的功能,系使用传递参数的方法来判断使用者,这部分请自行参考变化。
【结 论】 工厂模式的对象产生方式,可以动态产生,也可以静态建立,Delphi 中有两个对象可以很方便的存放这些事先建好的对象,一个是TForm ,一个TDataModule,在本范例中,是利用TDatamodule 存放事先建立好的对象,让Client 端可以很方便的取得这些对象。
|
|