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

探索设计模式

——星际争霸探险之旅

 

 

第一部分:创建模式

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值