`
IcyFenix
  • 浏览: 359073 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
文章分类
社区版块
存档分类
最新评论

探索设计模式之四——建造者模式

阅读更多

 

4.建造者模式(Builder Pattern

       GOF中各种构造模式的目的都是获取最终产品。在Terran章节中介绍了三种工厂模式的关注点仅仅是最终产品;而本章所介绍的建造者模式,关注的除了产品之外,它还关心着产品构造的细节,这一点是Builder模式和Factory模式最显著的区别。

 

       在上一章中,我们曾经说过“使用者通常是不关心产品具体成员的创建过程的,因此也不应当把各个产品的创建过程揉合到具体逻辑之中”。大多数情况确实是这样的,但是可以想象一些特殊情况:譬如你使用Builder模式去调配一杯鸡尾酒,应当希望可以调整内部各种掺酒的比例;又或者你使用Builder模式去装配一部汽车,不幸汽车出厂时发现质量有问题,你肯定希望能快速的诊断到底是装配的时候发动机出了问题,还是装配的轮胎或者挡风玻璃有问题。

 

目的:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

 

场景:

Sarah Kerrigan(剧情中Zerg的刀锋女王)的部落现在还弱小,而且正在受到Jim Raynor的持续压制,为了能扭转不利的局面,Kerrigan升级了Overlord的速度与运输技能,很明显,Kerrigan要空投了。

 

根据当前的科技状况,Kerrigan能掌握的部队有:

Overlord:领主,现在主要充当运输机的角色。

Zergling:小狗,Zerg的初级近战兵种。

Hydralisk:刺蛇,Zerg的初级远程兵种。

Lurker:潜伏者,Zerg的中级兵种,擅长范围攻击。

 

分析:

 

Kerrigan的空投策略是多点小规模同时骚扰,这需要多组空投部队(最终产品),每组空投部队都由一个Overload装在着,外表看起来是一样的,执行的行为也都是攻击、破坏、骚扰;但是我们可以根据战场实际情况,调整装配进各个Overload中的部队的细节,获得更好的效果。

 

当前为了演示需要,制定了SceneASceneB两种场景说适合的兵力配置,先看看以下UML图和代码:

 



 4.1 战斗部队及建造者的UML

 

下面是一个Zerg战斗部队(最终产品)的代码,这支部队各个兵种的数量是可调整的:

 

 

/**
 * Builder的产品,代表一组骚扰部队
 */
public class ZergProduct {
 
    private int overload;
 
    private int zergling;
 
    private int hydralisk;
 
    private int lurker;
 
    public int getOverload() {
        return overload;
    }
 
    public void setOverload(int overload) {
        this.overload = overload;
    }
 
    public int getZergling() {
        return zergling;
    }
 
    public void setZergling(int zergling) {
        this.zergling = zergling;
    }
 
    public int getHydralisk() {
        return hydralisk;
    }
 
    public void setHydralisk(int hydralisk) {
        this.hydralisk = hydralisk;
    }
 
    public int getLurker() {
        return lurker;
    }
 
    public void setLurker(int lurker) {
        this.lurker = lurker;
    }
 
    public String toString() {
        return "当前部队包括:" + overload + "个Overload; " + zergling + "个Zergling; " + hydralisk + "个Hydralisk; " + lurker + "个Lurker";
    }
}
 

 

 

接着,我们建立了2中不同的场景使用不同的空投部队去应对:

场景A空投了1Overload; 2Zergling; 1Hydralisk; 1Lurker

场景B空投了1Overload; 4Hydralisk

 

       有一点需要说明一下,在实际情况下,两种产品的不同一般不会仅仅体现在产品组成部分的数量变化上,事实上如果仅仅是数量的不一致,那可以把这种区别看作是装配期间才需要关心的职责,放入到Director中完成。这里的AB场景建造者只保留数量上的区别主要是为了简易起见,实际情况下我们可以在这里处理更本质的差异,譬如除了放入不同OverloadZergling个数不一样外,还可以根据不同情况将“吃粗粮长大的Zergling”、“吃奶粉长大的Zergling”放入不同的Overload中。

 

建立这2种建造者的实现代码如下:

 

 

 

/**
 * 适应场景A的装配器
 */
public class SceneABuilder implements ArmyBuilder {
 
    ZergProduct product = new ZergProduct();
 
    public void buildHydralisk() {
        product.setHydralisk(1);
    }
 
    public void buildLurker() {
        product.setLurker(1);
    }
 
    public void buildOberload() {
        product.setOverload(1);
    }
 
    public void buildZergling() {
        product.setZergling(2);
    }
 
    public ZergProduct getResult() {
        ZergProduct finlishProduct = product;
        product = new ZergProduct();
        return finlishProduct;
    }
}
 
/**
 * 适应场景B的装配器
 */
public class SceneBBuilder implements ArmyBuilder {
 
    ZergProduct product = new ZergProduct();
 
    public void buildHydralisk() {
        product.setHydralisk(4);
    }
 
    public void buildLurker() {
        product.setLurker(0);
    }
 
    public void buildOberload() {
        product.setOverload(0);
    }
 
    public void buildZergling() {
        product.setZergling(0);
    }
 
    public ZergProduct getResult() {
        ZergProduct finlishProduct = product;
        product = new ZergProduct();
        return finlishProduct;
    }
}
 

 

       同时,我们将具体的建造过程放到Director里面以便分离职责。这样当日后部队建造顺序需要调整,譬如要求先建造Zergling,再建立Hydralisk我们仅需要调整Director的内容。又或者产品的某一部分不需要或者不可用的时候,譬如当虫族Spawning Pool(血池,制造Zergling的必须建筑)被破坏之后,我们要达到不能再使用Zergling这种兵种的效果,就不需要修改每个具体的建造者,只需要在Director中把builder.buildZergling();这句去掉就可以生产出来的产品不再包含Zergling

 

Director的实现代码如下:

 

 

 

/**
 * 描述建造者的建造过程
 */
public class Director {
 
    public void construct(ArmyBuilder builder) {
        builder.buildOberload();
        builder.buildHydralisk();
        builder.buildZergling();
        builder.buildLurker();
    }
}
 

 

然后再看一下Builder模式是如何被使用的:

 

 

 

/**
 * 模拟建造两只部队
 */
public class WarField {
 
    public static void main(String[] args) {
        Director director = new Director();
        ArmyBuilder builderA = new SceneABuilder();
        ArmyBuilder builderB = new SceneBBuilder();
        director.construct(builderA);
        director.construct(builderB);
        System.out.println(builderA.getResult());
        System.out.println(builderB.getResult());
    }
}
 

 

从上面的过程可以看到,Builder模式与Abstract Factory模式从目的上讲是相当类似的,都是为了生产出复杂产品(两个例子的产品都是一组多兵种组合的协同小组),他们的主要区别就是Abstract Factory模式的建造过程完全封装在工厂之内,使用时调用工厂立刻获取到产品。而Builder模式将生产过程放在Director之中,可以精细控制最终产品的生产过程,使用的时候是一步一步的得到最终产品。

 

       最后,展示一下程序的最终运行结果:



  4.2 两种兵种组合

 

总结:

所谓设计模式就是人们总结出来,可以在特定上下文环境下解决一些常见问题的最佳实践。六种构造模式的目的都是生产产品,就像无论在Terran还是Zerg,使用的是何种方法都是需要生产部队才能赢得比赛,但只有根据种族的特点,采取最佳的营运与配搭,才能获得快速的发展。使用模式也一样,在实际编写代码时,我们是为了解决上下文遇到的问题而去使用模式,而不要脱离上下文、脱离具体问题为了使用模式而使用模式。

 

我看来,打星际争霸学会几种开局不难,难的是应变。把每种模式的框架背下来写代码中套用上也不难,真正掌握了模式的标准应当是使用的时候能否做到“顺势而动、行云流水”。

 

从本章开始介绍的三种Factory模式以外的构造模式,也让Zerg的发展正式走进了我们的剧情之中,现在让我们一起看看Zerg中一些很神奇部队生产方式,去学习下一种构造模式。

  • 大小: 11.5 KB
  • 大小: 22.7 KB
分享到:
评论
3 楼 macemers 2011-08-29  
PDF附件在哪里?
2 楼 sbpya 2010-04-30  
确实,学习模式关键的地方就在于如何恰当地运用到系统中去,而不能为了模式而模式。
1 楼 IcyFenix 2010-01-19  
PDF下载见附件,《设计模式探索——星际争霸探险之旅》其他章节请见我博客:
http://icyfenix.iteye.com

相关推荐

Global site tag (gtag.js) - Google Analytics