3.抽象工厂模式(Abstract Factory Pattern)
前面介绍的“简单工厂模式”和“工厂方法模式”,立足点都是避免显式的创建具体对象,封装创建对象时可能出现的变化点,这已经能比较好的解决单个对象创建的问题,但实际业务中,还经常出现需要一系列对象互相关联使用来完成任务的情况。对于存在关联、以来的产品来说,使用简单工厂或者工厂方法一个一个的创建其中的具体产品,经常无法达到理想的效果。譬如,“西装”和”短裤”都分别实现了“上衣”,“裤子”接口,分别套在身上一点问题都没有,但是实际上却不太可能穿着西装和短裤上街。
通过简单工厂或者工厂方法一个一个地单独创建产品然后再合并使用除了有可能出现上面“西服+短裤”的笑话外,用户在使用组合产品的时候,通常是不关心产品具体成员的创建过程的,因此也不应当把各个产品的创建过程揉合到具体逻辑之中。为了解决上面的问题,就GoF经典设计模式中就出现了本章的主角——抽象工厂模式。
目的:
创建一系列互相关联或者互相以来的组合产品。
场景:
随着游戏的发展,前期小打小闹的试探进攻时期已经过去。我们要考虑的问题也已经不再是一个机枪兵能否战胜一只小狗、一个火兵能否打赢一只刺蛇(见简单工厂模式)了。游戏中期要占得先机,关键不在一兵一卒,而在战术正确与否。
星际争霸多年的游戏发展中,出现过无数天马行空的战术和激动人心的战例。其中,在Terran在对抗Terran和Zerg时,有两种主流战术如下:
TvT:主力兵种使用坦克,维护兵种是SCV,空军选择Wraith(隐飞)
TvZ:主力兵种使用枪兵,维护兵种是Medic(护士),空军选择Science Vessel(科学球)
分析:
只要对手的种族一确定,我们的战术就定下来,也就是选择的兵种就定下了。出战的时候,都是框一圈兵A过去(-_-#),并不关心里面某个兵是什么时候、如何被生产出来的,关心的只是这组部队能不能优势互补,赢得战争。这符合抽象工厂的使用场景。现在,就通过抽象工厂模式,看看上述两种战术中的兵种组合是如何使用代码演绎的。
照例,我们先看一下这次需要涉及到的工厂和产品类图及代码:
图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("隐飞作为辅助单位,有优秀的侦查性能和很高的机动性。");
}
}
|
在上述代码中,“IAttackUnit、IAirUnit、IMedicalUnit”是抽象产品,实现这三个接口的“Tank、SCV、Marine……”等则是具体产品,关于抽象产品、具体产品等概念在前一章已经介绍过。现在我们继续看看如何通过工厂来为抽象产品选择具体的实现类的。
抽象工厂和具体工厂的实现代码如下:
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中发挥作用的。
- 大小: 7.7 KB
- 大小: 3.1 KB
- 大小: 2.5 KB
- 大小: 18.9 KB
分享到:
相关推荐
设计模式——抽象工厂模式源码,设计模式——抽象工厂模式源码
vs2010实现的软件设计模式——抽象工厂,是软件工程专业学生专业技能之一
写博客上传的代码:blog http://blog.csdn.net/t1234xy4/article/details/52424048
Android之大话设计模式——:抽象工厂模式借鉴.pdf
抽象工厂设计模式抽象工厂设计模式 抽象工厂设计模式 抽象工厂设计模式 抽象工厂设计模式 抽象工厂设计模式 抽象工厂设计模式
c#工厂模式——简单工厂_抽象工厂实例
Android之大话设计模式——:抽象工厂模式参考.pdf
IOS 设计模式抽象工厂实例Demo IOS 设计 模式 抽象 工厂
c#工厂模式 简单工厂,抽象工厂,单件模式
软件开发模式——三层抽象工厂模式.doc
设计模式专题之(三)抽象工厂模式---设计模式抽象工厂模式示例代码(python--c++)
设计模式 抽象工厂模式 三层架构 设计模式 抽象工厂模式 三层架构 设计模式 抽象工厂模式 三层架构 设计模式 抽象工厂模式 三层架构 设计模式 抽象工厂模式 三层架构 要您一个资源分,也算是对我的尊重,谢谢下载。
设计模式--抽象工厂模式实现多数据库连接 设计模式,.net设计模式,抽象工厂
现在需要设计一款格斗类游戏软件,为了适应不同等级玩家的需要,游戏特地安排了三个难度级别: ①初等级别:敌方士兵反应迟钝,怪兽步履缓慢,即便是初学者也能够很轻松地取胜; ②中间级别:敌方士兵反应适度,怪兽...
java设计模式【之】抽象工厂模式【源码】【场景:制作咖啡】 * 抽象工厂 * * 创建对象实例时,不要直接 new 类, 而是把这个new 类的动作放在一个工厂的方法中,并返回。 * 在工厂方法的基础上,进一步抽象工厂...
设计模式之工厂方法、简单工厂、抽象工厂
设计模式-抽象工厂模式(讲解及其实现代码)
java设计模式 抽象工厂模式详解 一张图让你彻底明白抽象工厂模式
C#简单登录演示抽象工厂设计模式,他具体而简单的把抽象工厂设计模式描绘出来了!
设计模式 抽象工厂模式 反射 配置文件 设计模式 抽象工厂模式 反射 配置文件 设计模式 抽象工厂模式 反射 配置文件 设计模式 抽象工厂模式 反射 配置文件 设计模式 抽象工厂模式 反射 配置文件 设计模式 抽象工厂...