Net框架中的设计模式之Builder(构造者)--兼谈抽象工厂和composite模式
上周三听了李建忠先生讲的23种面向对象的设计模式课程之builder(构造者),其中一些内容让我很有体会。
当然他所举的造房子的例子讲到最后让我感觉有点“脱靶”,偏离其初衷,这里先不急于解释。
在开始今天的正文之前,首先举两个例子,一个是古代的,一个是当下的。
例子1:(注:本例来自n年前博客园里一位仁兄的忽发奇想,本人觉得有一定道理,这里不妨引用一下)
话说“乾隆”准备派钦差大臣去出使“葛尔丹”,来安抚那里的“臣民部落”。而这个钦差大臣有两件事要做:一
是给那些部落金银珠宝,二是赏赐美女。而皇帝身边有两个大臣,分别是“何坤”和“纪晓岚”。这两位大臣的做事
风格相信大家早有耳闻,而乾隆也知道如果派何坤去,肯定是把金银珠宝他自已都贪了,美女他自己也会像韦
在这个例子中,乾隆这个king就是一个总导演,他决定这个“钦差大臣”由谁来担当,而这个决定取决于其自
己的政治意图(即是想安抚还是逼反“葛尔丹”)。
而就是乾隆就是builder模式中的Director, 而“复杂对象的算法”则是那一整套定国安邦的基本国策。而"钦
差大臣"大臣就是Builder类。最后“何坤”和“纪晓岚”分别就是相应的 ConcreteBuilderA及 ConcreteBuilderB。
而赏赐“金银珠宝”和“美女”分别就是“BuildPartA”及“BuildPartB”的方法(操作)了。最终的系统结构如下图所
示:
当然如果大家感觉这个例子不是与时俱近,起码跟大家当下的日常生活内容不粘边。下面再举个例子。
例子2:
相信前一阵子“热播”的央视版“鹿鼎记”大家都看了吧。其实这个电视剧中从导演到演员,以及最终的剧本我们
可以将其看成是一个builder模式。其中剧本就是“复杂对象的算法”,因为其基本上相对稳定 (尽管大胡子导演改
动了一些),但内容非常复杂(起码我看得很晕)。而张大导演就是Director,而“韦小宝”这个小说中的角色就是
Builder(抽象类),而黄晓明就是其中的一个实现类ConcreteBuilderA,当然ConcreteBuilderB是其它可能出
演这个角色的演员,如:陈佩斯,陈小春,梁朝伟等。而导演的意图决定了谁最终出演男一号。而这个意图可能
就是将这部电视拍成:
而其关系图如下所示:
下面是广告时间:
胡八一答曰:“他娘的鬼写的,鬼知道干什么用!!!”
----摘自<<鬼吹牛软件培训中心文摘>> 第****期
好了,到这里我们将 builder 还以本来“颜色”。下面是其意图:
意图: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性: 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
下面是其类关系图:
通过将“对象的构建与它的表示分离”,使系统中变化的部分(“ConcreteBuilderA”)与复杂但相对稳定的
算法“解耦”,使两者各自独立。这样程序员就可以专心设计开发builder及其派生类,完成各种对象的表示功能。
其实在李建忠先生讲解这个模式时,用了一个不错的例子来说明,就是“盖房子”。
如大家所知,“盖房子”这个工作本身是一项土木工程,有严格的标准,且施工过程有一系列的指标和先后
顺序。我们可以将盖房子本身看成是一整套复杂算法。其虽复杂但却有章可循,常识是先打地基(正负0),再
垒墙,上柁,檩,椽子,盖板等,最后是门,窗户。当然垒墙其间还要预留线槽,上下水等设置。这套过程不
管是造小房子或别墅等应该都是大同小异。因为虽然繁琐但却很稳定,顺序很少发生变化(至少我没听说有人
是先造屋顶再打地基的,不过“鸟巢”可能是分开同时进行最后组装的,这里就不瞎扯了,呵呵)。
这里造窗子,门,墙等就是builder一级的抽象类,其定义如下:
{
abstract public void BuildDoor();
abstract public void BuildWindow();
abstract public void BuildWall();
}
而具体的实现类就是想要造什么样(风格)的Door,Window,Wall。相信聊到这里大家应该清楚接下来该如
何设计了。
在课程的最后李建忠先生举了一个在.net中的builder模式的例子,让我有茅塞顿开的感觉。他说在我们进行web
开发中,经常使用的Page继承就是一个builder模式的运用。请大家先看一下这个类的实例代码(摘自DiscuzNT后台
管理页面基类):
{
/// <summary>
/// 控件初始化时计算执行时间
/// </summary>
/// <param name="e"></param>
protected override void OnInit(EventArgs e)
{
m_processtime = (Environment.TickCount - m_starttick) / 1000;
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
if (this.IsRestore)
{
ArrayList modifiedControls = new ArrayList();
foreach (string key in PostData.AllKeys)
{
System.Web.UI.Control control = FindControl(key);
if (control is IPostBackDataHandler)
if (((IPostBackDataHandler)control).LoadPostData(key, PostData))
modifiedControls.Add(control);
}
// 发生 PostDataChanged 事件在所有已变动的控件上:
foreach (IPostBackDataHandler control in modifiedControls)
control.RaisePostDataChangedEvent();
}
base.OnLoad(e);
//
}
}
在上面代码中,AdminPage就是builder模式中的ConcreteBuilder,而其重写的的OnInit,OnLoad等方法(多数以“On”
做为前缀)即是ConcreteBuilder类中的BuilderPart。我们在这里定义相应的方法,最终由.net框架按照页面生存周期(相当
于builder模式中那套复杂但稳定的算法) 来顺序进行调用。而我们要做的就是在相应的页面(aspx上的)声明相应的类即可,
比如:
<%@ Page Language="C#" Inherits="Discuz.Web.Admin.editforumaggset" %>
而Inherits="Discuz.Web.Admin.editforumaggset"即是这个声明。
注:editforumaggset继承自System.Web.UI.Page.
builder模式中的Builder类应该指的就是System.Web.UI.Page类了。
看到这里,相信大家应该也会有所体会了。
下面继续聊一下李建忠先生在这一节课程最后总结builder模式与abstract factory(抽象工厂)模式之间区别的
话题。
首先请看一下关于abstract factory的一些设计要点:
1.如果没有应对“多系列对象构建”的需求变化,则没有必要使用抽象工厂模式,这时 候使用简单的静态工厂
完成可以。
2.“系列对象”指的是这些对象之间有相互依赖,或作用的关系,例如游戏开发场景中 的“道路”与“房屋”的依
赖,“道路”与“地道”的依赖。
3.Abstract Factory 模式主要在于应对“新系列”的需变动。其缺点在于难以应对“新对象”的需求变动。4.Abstract Factory 模式经常和Factory Method(工厂方法)模式共同组合来应对“对象创建”的需求变化。
接着是李建忠先生所总结的关于Builder模式的要点:
Builder 模式:
1.分步骤构造一个复杂对象,其中的分步骤是一个稳定算法,而复杂对象的各个部分经常变化。
2.其应用复杂对象的各个部分频繁需要变动,其缺点在于难以应对“分步骤构建算法”的需求变动。
3.abstract factory解决系列对象的需求变化(这种变化是系列对象整体的变动。如果添加对象则不适用,且
系列对象内部之间有关联关系,如树,路),builder模式解决对象部分的需求变化。BUILDER模式通常和
COMPOSITE模式组合使用。(这里是我个人观点:创建房子过程中,各个对象之间没直接关系,而都是房
子的"子对象")
其实在这节课程的进行到一半的时候,我就在想这两个设计模式之间的一个问题了。诚然,在我以前的设计中
用过"抽象工厂"模式,所以才突然感觉到这两个模式之间是存在一定关系的,当然如果区分不开的话,也很容易混
到一起,到时只有自已头晕的份了。
就我个人而言:
abstract factory模式之所以不支持“新对象”的需求变动,主要是其产品层面上被抽象出来了一层,即:
abstractproduct.当新增一种产品时,这部分变成的会很剧列,而相应的createFactory部分也会随着产生变化。
当然最糟的是client使用了abstract product 的引用,所以它这里变动的可能性也会随即上升。这恰恰是abstract
factory模式错误使用的“兆头”。最后整个架构都会随之变动,可谓“天翻地覆”了。
当然,我个人认为Builder模式是支持“新对象”的需求变动。因为其product类并未被直接使用,而是间接通过
ConcreteBuilder接供,其变动已被ConcreteBuilder封装,所以当新对象添加进来时,变化都发生在了类关系图
的右侧。
而builder模式与Composite模式一起使用应该很好理解,因为 Composite通常是将对象组合成树形结构以表
示“部分-整体”的层次结构, 如下图:
使得用户对单个对象和组合对象的使用(Operation)具有一致性。而在上面造房子的例子中我们可以将其中的一个
垒墙的过程进行“放大”。考虑如下情况:垒墙之前要搅拌水泥,备砖等前期准备工作,而垒墙完成后还要溜缝或贴磁
砖。这一系列的操作可能从属于垒墙这个整体操作,而这些工作又缺一不可,所以可以使用Composite模式来完成部
分甚至下脚料的操作。从而使整体操作有统一的设计和调用接口。
当然如果大家不认同这个解释,我们可以找一下更大的例子:“造(核动力)航母”,这个由成千上万零部件组装在
一起的整体成果,每个大的零部件都可以划分成无数个小的部件(直到镙丝镙拇)。这本身就是一个builder模式与
Composite模式的典型应用。
builder模式用于解决大个部件的设计建造过程,因为造航母本身这个复杂的对象算法是很少变化的(造同级别的常
规动力和核动力航母的过程应该相似,甚至部件都可相互替换。只是反应堆的使用会进一步减少存放燃料的空间,从
而增加航载机的数量)。而Composite负责将复杂部件的构造过程进一步细分到可以通过小部件进行组装完成。
结束语:
设计模式快有两年多没怎么研究了,只是最近听的课程又让我有机会温故并知新,而研究这个东西本身相
信是一个要长期不懈学习的过程,估计到死时还要拿着GOF的书进火葬场了。
好的,今天的内容就先到这里了。
感兴趣的朋友可以在回复中讨论:)
tag: builder,abstract factory, composite
作者:daizhj,代震军
原文链接:http://www.cnblogs.com/daizhj/archive/2008/08/15/1268396.html