探索设计模式之二——工厂方法模式

2.工厂方法模式(Factory Method Pattern

       上一章介绍了简单工厂模式的作用和使用场景,它能为我们隔离开产品使用者与产品制造者的职责,使得这两部分代码脱离耦合。

 

       在看到简单工厂模式带来的好处的同时,我们也可以预见,随着工厂产品的丰富,每增加一个产品,都将需要修改工厂方法。这是简单工厂的特性——只能处理可预见的情况决定的。然而,频繁的修改代码是我们所不愿意看到的,通过修改代码来增加产品(功能),也违反了设计模式中的“开闭原则” 。

      

       开闭原则:即“开放-闭合原则”,软件架构应该设计成对于修改来说,代码实体是关闭的,而对于扩展来说,代码实体则是开放的。通俗一点讲,开闭原则是鼓励通过“新增”来代替“变化”。鼓励设计者将业务逻辑抽象化,将逻辑的本质设计成抽象类或者接口,逻辑的具体实践操作设计成具体实现类。

 

现实世界中,业务的本质是相对稳定的,而业务的具体实践则总会不断变化。在逻辑改变的时候,应当做到可以只替换具体实现类,而会不影响到代码的架构。开闭原则是OOAD中的核心原则,在实现功能需求的时候,新增的代码与修改的代码之间的比值,某种程度上可以作为量化衡量一个软件系统的成熟程度的标志。

 

       为了解决简单工厂的这个弊端,需要引入本章的主题:工厂方法模式。工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor),工厂方法模式主要是对“工厂”的封装,提供一个创建者的接口,将产品实例化的步骤延迟到工厂的子类完成,让子类决定需要创建什么产品。

 

目的:

定义创建者的抽象接口,延迟产品实例化到其子类之中,创建的产品一般不再通过传入参数控制,而直接决定于使用了哪个实现类作为其子类。

 

场景:

       随着游戏的发展,只靠机枪兵、喷火兵越来越难以获得战斗胜利了。这时候,机械化的重型武器登上了战争的舞台,战场上的主角,不再只是从兵营一个地方生产出来。司令部为了检验Raynor的设计模式是否能在机械化混合部队中发挥作用,特别举行了一次阅兵仪式……

 

为了简单起见,本次阅兵需要检阅仅两种部队:从兵营生产出来的机枪兵以及从重工厂生产出来的坦克。无论从哪里生产出来,他们都是Terran的部队,实现了ITerranArmy接口,对于本次阅兵来说,此接口只有一个showState()方法。

 

                              

分析:

 

       照例,我们先看一下这次需要涉及到的产品类图及代码:

2.1 战斗部队及工厂的UML

 

public interface ITerranArmy {
    public void showState();
}
 
public class Marine implements ITerranArmy {
    public Marine() {
        System.out.println("训练出一个机枪兵");
    }
 
    public void showState() {
        System.out.println("机枪兵从兵营生产,属于步兵,作战灵活。");
    }
}
 
public class Tank implements ITerranArmy {
    public Tank() {
        System.out.println("制造出一架坦克");
    }
 
    public void showState() {
        System.out.println("坦克从重工厂生产,属于机械部队,威力强大。");
    }
}
 

 

       如果现在继续使用简单工厂来完成这个需求的话,随着各种兵种的登场,工厂类的代码将会出现一长串的if语句,而且每增加一种兵种,就必须修改一次工厂类。显然,根据上面的讨论,这样的代码是不可忍受的。

 

       根据当前的实际情况,Raynor对简单工厂做出了一些扩展:首先建立后勤部,它不负责生产任何部队,但是任何部队的生产都是由后勤部直接领导,在程序语言中,我们把它叫做“抽象工厂”,在本次实例中,我们把它叫做“ITerranFactory”接口,无论是兵营还是重工厂,都是此接口的一个实现类,在程序语言中,我们把这些实现类叫做“具体工厂”。而场景描述中提到的ITerranArmy接口,在程序语言中则成为“抽象产品”,要生产的机枪兵、坦克都是此接口的实现,成为“具体产品”。

 

       抽象工厂和具体工厂的实现代码如下:

 

//抽象工厂
public interface ITerranFactory {
    public ITerranArmy createArmy();
}

//具体工厂,此工厂生产坦克
public class MachineFactory implements ITerranFactory {
    public ITerranArmy createArmy() {
        return new Tank();
    }
}

//具体工厂,此工厂生产机枪兵
public class SoldierFactory implements ITerranFactory {
    public ITerranArmy createArmy() {
        return new Marine();
    }
}
 

 

       在阅兵的时候,调用代码如下:

 

public class WarField {
    public static void showState(ITerranFactory factory) {
        factory.createArmy().showState();
    }
 
    public static void main(String[] args) {
        // 一般来说,大多应用是将Factory的实现类写在配置文件中。
        // 这样就可以实现通过更换工厂来切换生产产品,而不用修改代码。
        showState(new SoldierFactory());
        showState(new MachineFactory());
    }
}
 

 

 

       从代码上可以看出,我们需要什么产品,不再是靠外界传入参数决定,而是取决于抽象工厂使用的是具体哪个实现类。

 

值得指出的是,虽然示例代码中是直接通过new来创建具体工厂,但在现实编程中,大部分工厂方法模式都是配合这反射来使用的,既可以考虑把“SoldierFactory”、“MachineFactory”等具体实现类写在配置文件中,通过反射代替直接new一个具体工厂对象,这样当增加一个产品,就可以做到对工厂和使用者都不存在任何修改,只需要添加一个新的工厂类,并且配置到配置文件中即可,这就是开闭原则的一个体现。

 

       最终运行结果如下:



 图 2.2 阅兵仪式运行结果

 

总结:

通过上述阅兵的例子,我们看到工厂方法模式确实给系统架构带来强大而灵活的扩展能力。这种基于多态性设计的模式,使得产品的实例化延迟到具体子类之中,通过替换不同的实现子类就能达到新增和切换产品的目的。

 

       由于本次阅兵效果良好,司令部决定让Raynor领导这些部队对Zerg进行一次大规模的协同作战。前面在简单工厂、工厂方法两种模式中,主要都是围绕这个体产品(MarineFirebatTank等)的创建进行讨论,下一章中,我们将会通过一次“协同作战”看看各种具体对象之间存在互相依赖关系时,如何运用抽象工厂模式(Abstract Factory Pattern)来解决问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值