探索设计模式之三——抽象工厂模式
3.抽象工厂模式(Abstract Factory Pattern)
创建一系列互相关联或者互相以来的组合产品。
?
随着游戏的发展,前期小打小闹的试探进攻时期已经过去。我们要考虑的问题也已经不再是一个机枪兵能否战胜一只小狗、一个火兵能否打赢一只刺蛇(见简单工厂模式)了。游戏中期要占得先机,关键不在一兵一卒,而在战术正确与否。
?
星际争霸多年的游戏发展中,出现过无数天马行空的战术和激动人心的战例。其中,在Terran在对抗Terran和Zerg时,有两种主流战术如下:
?
TvT:主力兵种使用坦克,维护兵种是SCV,空军选择Wraith(隐飞)
TvZ:主力兵种使用枪兵,维护兵种是Medic(护士),空军选择Science Vessel(科学球)
照例,我们先看一下这次需要涉及到的工厂和产品类图及代码:
图3.1 战斗部队及工厂的UML图
public interface IAttackUnit { // 主攻单位有attack技能 public void attack();} public interface IAirUnit { // 空中单位有assist技能 public void assist();} public interface IMedicalUnit { // 医疗单位有cure技能 public void cure();} public class Marine implements IAttackUnit { public Marine() { System.out.println("制造出一个枪兵。"); } public void attack() { System.out.println("机枪兵作为主攻兵种,适合应付小体积单位,作战灵活,能被护士MM治疗。"); }} public class Medic implements IMedicalUnit { public Medic() { System.out.println("制造出一个护士。"); } public void cure() { System.out.println("护士能给生物部队快速恢复生命值,对机械部队着无能为力。"); }} public class ScienceVessel implements IAirUnit { public ScienceVessel() { System.out.println("制造出一个科学球。"); } public void assist() { System.out.println("科学球作为辅助单位,能侦查隐性单位,能使用辐射技能对目标造成伤害。"); }} public class SCV implements IMedicalUnit { public SCV() { System.out.println("制造出一个SCV。"); } public void cure() { System.out.println("SCV能修理坦克,但是对机枪兵等生物部队无能为力。"); }} public class Tank implements IAttackUnit { public Tank() { System.out.println("制造出一辆坦克。"); } public void attack() { System.out.println("坦克作为主攻兵种,适合应付大体积单位,威力强大,能比被SCV修理。"); }} public class Wraith implements IAirUnit { public Wraith() { System.out.println("制造出一架隐性飞机。"); } public void assist() { System.out.println("隐飞作为辅助单位,有优秀的侦查性能和很高的机动性。"); }}?
public interface IArmyFactory { // 创建空军单位 public IAirUnit createAirUnit(); // 创建医疗单位 public IMedicalUnit createMedicalUnit(); // 创建主攻单位 public IAttackUnit createAttackUnit();} public class TvTArmyFactory implements IArmyFactory { public TvTArmyFactory() { System.out.println("当前使用的战术可以应付TvT。"); } // 空中单位是隐飞 public IAirUnit createAirUnit() { return new Wraith(); } // 主攻单位是坦克 public IAttackUnit createAttackUnit() { return new Tank(); } // 医疗单位是SCV public IMedicalUnit createMedicalUnit() { return new SCV(); }} public class TvZArmyFactory implements IArmyFactory { public TvZArmyFactory() { System.out.println("当前使用的战术可以应付TvZ。"); } // 空中单位是科学球 public IAirUnit createAirUnit() { return new ScienceVessel(); } // 主攻单位是枪兵 public IAttackUnit createAttackUnit() { return new Marine(); } // 医疗单位是护士 public IMedicalUnit createMedicalUnit() { return new Medic(); }}?
public class WarField { // 战斗场景 public static void warScene(IArmyFactory factory) { factory.createAttackUnit().attack(); factory.createMedicalUnit().cure(); factory.createAirUnit().assist(); } public static void main(String[] args) { warScene(new TvTArmyFactory()); System.out.println(); warScene(new TvZArmyFactory()); }}?
?
从代码上可以看出,每一个具体工厂的目的都不是创造单个的具体产品,而是过把整个产品家族(IAttackUnit、IAirUnit、IMedicalUnit)的所有具体产品都创造出来,这样各个具体产品之间的联系就通过一个具体工厂来体现。当需要新增一个产品家族,或者已有产品家族的构成成员发生变化的时候,影响也就局限在了某一个具体工厂之中。业务逻辑所依赖的都是抽象工厂和抽象产品,具体产品变化的影响就这样从业务逻辑中隔离开来。
?
这种通过依赖抽象类(或者接口)来削弱耦合、隔离变化点的方法,在设计模式中被总结为一个重要原则——依赖倒转原则:业务逻辑应该依赖于抽象类,而不应该依赖于具体实现类。“依赖倒转”中的“倒转”,指得就是人们直观思维之中,高层逻辑应当依赖与他下面的功能点分解,功能点分解又依赖于底层功能这种自上而下层层依赖的开发思想。这种开发思想中,任何底层的变化都有可能导致产生对上层的影响,同时,上层需求有改动的时候,又同样需要下层改动提供支持,这样当软件层次累计得越来越高的时候,变化就越来越可能引起上下层间的反复传递,导致严重的后果——牵一发而动全身。
图3.2 自上而下的依赖关系
?图3.3 依赖反转
?图 3.2 两种兵种组合
经过三章关于“工厂模式”的介绍,我们对通过工厂来创建产品,隐藏具体实现和创建细节的做法已经有了一定的了解。对象的产生,除了直接使用new和使用工厂封装外,还有几种其他方式。在这边Terran部队建设如火如荼的时候,且看看那边Zerg的发展情况如何,看看其他几种创建模式是如何在Zerg中发挥作用的。
1 楼 IcyFenix 2010-01-19 PDF下载见附件,《设计模式探索——星际争霸探险之旅》其他章节请见我博客:http://icyfenix.iteye.com 2 楼 fengsage 2010-01-22 谢谢分享。能否这样理解,普通工厂模式是抽象工厂模式的一种特例?
楼主能否用项目实例来阐述下在哪些情况下运用相对应的设计模式? 3 楼 mingyang2013 2010-06-17 很不错,多谢楼主!有全部23种设计模式的下载吗?如有,请告知,谢谢。 4 楼 yuur369 2012-02-02 很简洁明了。。。但是初学者不一定能看懂。。要有一定项目实际经验的人才能快速理解。。。