论坛首页 Java企业应用论坛

探索设计模式之一——简单工厂模式

浏览 8242 次
精华帖 (1) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-01-19   最后修改:2010-01-19

探索设计模式

——星际争霸探险之旅

 

 

第一部分:创建模式

       GoF23种经典设计模式中,按照行为目的划分的话,可分为“创建模式”、“结构模式”、“行为模式”三大类别。这三大类23种设计模式,将会在《设计模式探索》中,随着Terran Zerg两个种族进行星际争霸[1]大战中,各个角色使用不同的设计模式来解决战场上各种各样的问题,一一展现设计模式在编程实践中,让代码变得优雅、健壮的魅力。

 

创建模式包括:

 

Factory Method

工厂方法模式

Abstract Factory

抽象工厂模式

Builder

构造器模式

Protoype

原型模式

Singleton

单例模式

 

1.简单工厂模式(Simple Factory Pattern

       严格来说,简单工厂模式并不属于GoF23种经典设计模式之一,但是简单工厂作为一个常用的编程习惯,在实际程序开发中经常被使用到,能为我们简单而有效的解决一些问题。同时,作为后续其他创建模式的引导,有必要先讲一下。

 

目的:

通过一个外界参数来选择不同类的实例。

 

场景:

       在《星际争霸》中,Jim Raynor(游戏剧情中的人族英雄)现在担任一个Terran突击小分队的队长,在与Zerg的战斗中,执行侦查和歼灭落单敌人的任务。根据当前的敌军的兵种状况,Terran后勤部门能提供两种战斗部队:Marine(机枪兵)和Firebat(喷火兵),分别能对ZergHydralisk(刺蛇)和Zergling(小狗)造成极大的伤害。但是如果顺序反过来,则喷火兵无法战胜刺蛇,机枪兵也无法战胜小狗。

 

分析:

 

       先介绍两种战斗兵种的UML图和代码,本文后面讨论的“产品”就是指代他们。

1.1 战斗部队(产品)的UML

 


 

       最初,Raynor直接向后勤部门申请了3个机枪兵和2个喷火兵的固定部队来面对战场的情况(战场用一个随机数字来模拟遇到的敌军)。如果用这种方式进行战斗的话……

 

public class WarField {
    // 随机模拟一个敌人
    public static int meetEnemy() {
        return (int) Math.round(Math.random());
    }
 
    // 战斗场景1
    public static void scene1() {
        ITerranSoldier[] solder = new ITerranSoldier[5];
        solder[0] = new Marine();
        solder[1] = new Marine();
        solder[2] = new Marine();
        solder[3] = new Firebat();
        solder[4] = new Firebat();
        solder[0].attackEnemy(meetEnemy());
        solder[1].attackEnemy(meetEnemy());
        solder[2].attackEnemy(meetEnemy());
        solder[3].attackEnemy(meetEnemy());
        solder[4].attackEnemy(meetEnemy());
    }
 
    public static void main(String[] args) {
        scene1();
    }
}
 

 

结果只能考验运气,运气不好的时候,下场那是相当的凄凉……



  1.2

 

显然,以固定的业务逻辑应付不断的业务变化是不可能完成任务的。为了掌握主动,夺取胜利,我们必须根据不同的敌人来选择不同的战士,现在,代码变成这样:

 

    // 战斗场景2
    public static void scene2() {
        ITerranSoldier[] solder = new ITerranSoldier[5];
        for (int i = 0; i < 5; i++) {
            int enemy = meetEnemy();
            if (enemy == Const.Zergling) {
                solder[i] = new Firebat();
            } else {
                solder[i] = new Marine();
            }
            solder[i].attackEnemy(enemy);
        }
    }
 

 

       这样的代码,看起来似乎能达到我们的目的,但是我们写代码,追求的不应当仅仅是解决一个具体问题,而是追求优雅和高质量的代码。当前,代码中一个很明显的问题就是,我们无法分辨Raynor到底是一个战队小队指挥官还是后勤部长?一个战场指挥官的职责是关心士兵如何战斗,而不是去关心士兵装备的是机枪还是喷火器,那是后勤部长的职责!如果将这两项职责混合到同一个人(同一段代码)中,日后无论是遇到新的敌人(业务逻辑的改变),还是有了新的士兵或者士兵要装备新的装备(产品的增加和产品变化)都需要修改这段代码。

 

       在长期的生产实践之中的之中,有许多被实践证明有益的设计原则,其中有两条应该在写类似代码的时候想起:“简单职责原则:一个类负责一件事情,一个类应该只有一个能引起他变化的因素。”“变化点隔离原则:如果一块代码不同地方都有可能经常发生改变,则说明它们应该分开封装,同时避免超大类、超大方法的产生。”

 

       基于上面两个原则,我们应该把指挥官和后勤部长分开,各自完成自己的任务,代码变成这样:

 

// 首先增加一个士兵训练工厂
public class SoldierFactory {
    public ITerranSoldier create(int enemy) {
        if (enemy == Const.Zergling) {
            return new Firebat();
        } else {
            return new Marine();
        }
    }
}
 
    // 战斗场景3
    public static void scene3() {
        // 定义一个兵营为“士兵工厂”,这也符合现实逻辑
        SoldierFactory Barrack = new SoldierFactory();
        ITerranSoldier[] solder = new ITerranSoldier[5];
        for (int i = 0; i < 5; i++) {
            int enemy = meetEnemy();
            solder[i] = Barrack.create(enemy);
            solder[i].attackEnemy(enemy);
        }
    }
 

 

现在的运行结果:



1.3

 

总结:

       从上面一个如何构建5人突击小队的编码过程中,我们看到简单工厂模式的优点:隔离产品创建者和使用者,明确各自的职责,降低耦合度。简单工厂模式的应用场景:当使用者知道工厂需要的传入参数,并且对工厂如何生产出产品的逻辑并不关心。

 

       简单工厂模式能为我们处理一些很经常遇到的代码场景,但同时要引用上文的一句话“以固定的业务逻辑应付不断的业务变化是不可能完成任务的”,设计模式也是如此,任何一个模式都不能解决所有的问题。现在游戏才刚刚开始,随着星际争霸战争过程的发展,后面将会遇到越来越复杂的场面。

 

同时,Raynor使用了设计模式,在小规模战斗中表现出色,升级为更高级军官,掌握更多的战斗部队,面对更多不同的敌人时候,简单工厂的缺点就逐渐显露出来:譬如只能根据事先考虑到的敌人来只能创建事先考虑到的部队、譬如随着机械化部队的加入,要通过单一的一个工厂来生产产品就显得无能为力……且看下一章,如何使用工厂方法模式(Factory Method Pattern)来解决这些问题。



[1] 《星际争霸》:著名电子竞技游戏。如果你接触过《星际争霸》这款游戏,那学习到知识的同时将会获得游戏的乐趣,而没有接触过《星际争霸》的话也没关系,没有游戏背景完全不影响对此系列文章的阅读。

 

  • 大小: 3.1 KB
  • 大小: 6.8 KB
  • 大小: 6.5 KB
   发表时间:2010-01-19  
PDF下载见附件,《设计模式探索——星际争霸探险之旅》其他章节请见我博客:
http://icyfenix.iteye.com
0 请登录后投票
   发表时间:2010-01-19  
IcyFenix 写道
PDF下载见附件,《设计模式探索——星际争霸探险之旅》其他章节请见我博客:
http://icyfenix.iteye.com

这些文章05年就有了

lz是转帖吗?
如果是为什么没有注明出处??

http://terrylee.cnblogs.com/archive/2006/02/21/334911.html
0 请登录后投票
   发表时间:2010-01-19  
kimmking 写道
IcyFenix 写道
PDF下载见附件,《设计模式探索——星际争霸探险之旅》其他章节请见我博客:
http://icyfenix.iteye.com

这些文章05年就有了

lz是转帖吗?
如果是为什么没有注明出处??

http://terrylee.cnblogs.com/archive/2006/02/21/334911.html


谢谢关注。

文章是原创的,还打算连载下去。
0 请登录后投票
   发表时间:2010-01-20  
那就,支持lz

感谢分享,06年的时候,我也讲过一段时间设计模式。
有时间可以交流下。
0 请登录后投票
   发表时间:2010-01-22  
想问楼主一个问题:

ITerranSoldier soldier=new SoliderFactory.create(enemy)

ITerranSoldier soldier= enemy==Const.Zergling?new Firebat(): new Marine();
这两种写法有何区别?引入这个工厂,有何好处?这个工厂真的是必须的吗?
0 请登录后投票
   发表时间:2010-01-22  
wandou 写道
想问楼主一个问题:

ITerranSoldier soldier=new SoliderFactory.create(enemy)

ITerranSoldier soldier= enemy==Const.Zergling?new Firebat(): new Marine();
这两种写法有何区别?引入这个工厂,有何好处?这个工厂真的是必须的吗?


你好,文中提到的这段话:

我们无法分辨Raynor到底是一个战队小队指挥官还是后勤部长?一个战场指挥官的职责是关心士兵如何战斗,而不是去关心士兵装备的是机枪还是喷火器,那是后勤部长的职责!如果将这两项职责混合到同一个人(同一段代码)中,日后无论是遇到新的敌人(业务逻辑的改变),还是有了新的士兵或者士兵要装备新的装备(产品的增加和产品变化)都需要修改这段代码。

只有Firebat和Marine两个兵种的话可能看不出差别,但是区分职责还是应当的,试想日后发展到有十几二十个兵种可供选择呢?如果下面一种写法创建的地方很多,发现需要增加或者减少一种兵种的时候是不是也麻烦?
0 请登录后投票
   发表时间:2010-01-25  
没有顿开茅塞的感觉,看来还需要修炼。
0 请登录后投票
   发表时间:2010-03-12  
想问楼主一个问题:

简单工厂模式和工厂模式在MVC开发中最常用的地方,和方法是什么啊?
0 请登录后投票
   发表时间:2010-05-08  
以前学习的时候感觉有点不解,看完LZ的文章,清晰不少
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics