设计模式中的每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。
一个设计模式,它的服务对象是高层模块,在设计模式中称为客户端,因此在描述设计模式的时候都是以客户端作为使用方来进行描述的。
设计模式在类间关系这个粒度上给出常见问题的解决方案。属于软件工程中逻辑架构设计中相当重要的一环。
快速查阅各类设计模式使用场景可参考此文:设计模式大全。
一、抽象工厂模式
定义:抽象工厂模式提供了一种方法,把那些拥有公共主题的工厂组合起来,并且在使用时不需要考虑他们生产的是哪个具体类的对象。(The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.)
概述:该模式是工厂模式的延伸。先介绍一下产品等级结构与产品族的概念,如下图。假设某个软件能够显示方、圆、菱形。本来这几种形状是很朴素的,黑白的,现在用户觉着太丑,换了一套拟人化的皮肤来显示方、圆、菱形。分别是方肘子、圆大头、菱妹妹。一套皮肤就是一个产品族,同一类形状就是一个产品等级结构。言归正传,抽象工厂模式的存在使得高层模块只需要给出具体的产品族和产品等级结构(如拟人、方)这两个参数,就可以得到对应的实例。
1、核心思想
高层模块只需要知道自己是哪个产品族的,藉此确定具体工厂。再通过具体工厂获取对象的时候不需要知道这个对象是属于哪个具体类的,具体工厂决定使用哪个具体类。
2、类图
- 抽象工厂(AbstractFactory):确定工厂的业务范围。
- 具体工厂(ConcreteFactory):每个具体工厂对应一个产品族。具体工厂决定生产哪个具体产品对象。
- 抽象产品(AbstractProduct):同一产品等级结构的抽象类。
- 具体产品(ConcreteProduct):可供生产的具体产品。
该模式的代码比较简单,不在此贴出,如需要可前往C++设计模式——抽象工厂模式观摩。
3、应用场景以及优缺点
1)应用场景
- 需要将对象的创建和使用解耦的场合。
- 且,系统有多个产品族,每次只使用其中一个产品族的产品。
- 且,产品等级结构稳定,不会增删新旧产品等级结构。
2)优点
- 将一系列具有某种共性的对象统一在一起,使得选择提前。例如软件的皮肤只需要在开始的时候做出选择,之后画方画圆的时候都不需要再考虑选择的问题了。
- 高层模块可以方便地通过更换具体工厂来改变所需创建的产品对象类型。
- 具体工厂可以方便地更换想要创建的产品类型。
- 增删产品族十分方便。
3)缺点
缺点:增删产品等级结构十分麻烦。需要产品等级结构足够稳定。
解决方案:将结构与行为分离,将产品族和产品等级结构转化为数据结构进行管理。(待补充实例)
二、游戏应用
1、适用场景
- 游戏难度、游戏阵营。不同级别难度下游戏可以产生不同类型的建筑、英雄以及技能。
- AI 战术选择。实现确定好几种可选战术作为产品族,每种战术按照类似海军、空军、陆军作为产品等级结构选择具体兵种进行生产建造。
- 国际化。用户改变语言环境时,会改变响应的文字显示,音效,图片显示,操作习惯适配等。
- 皮肤更换或者资源管理。用户选择不同主题的皮肤将会影响图片显示以及动画效果。
- 屏幕适配。对高、中、低分辨率的移动设备使用不同的显示资源。
2、具体案例
抽象工厂在游戏中的使用更多的是一些与游戏机制无关的部分。仅有部分游戏机制会需要用到抽象工厂模式。笔者考虑了一下,觉得还是举一个常用的具体案例具有更加通用并且广泛的价值。
一般游戏都会考虑使用两种语言,中文&英文。如果不使用抽象工厂,那么在任何需要区分中文和英文的情况下都需要做一次选择,多种语言时更是复杂。如果在类设计过程中预感这种选择会很多,并且不会再增添新的语言。那么可以考虑使用抽象工厂模式。假设游戏的文字和语音这两个方面支持双语。
1)伪代码
////工厂//////////////////////////////////////
class 国际化{
public:
文字()=0;
语音()=0;
};
// 产品族1
class 汉语: public 国际化{
public:
文字 文字(){
return new 汉语文字(); // 选择合适的具体产品创建实例
};
语音 语音();
};
// 产品族2
class 英语: public 国际化{
public:
文字 文字();
语音 语音();
};
////产品//////////////////////////////////////
// 产品等级结构1
class 文字{
public:
文字();
};
class 汉语文字: public 文字{
public:
汉语文字(){
加载汉语文字资源();
};
public:
string 获取文字(int 章节,string 人物){
static int cnt = 0;
string text = texts[章节][人物][cnt];
cnt++;//切换到下一句
return text;
};
};
class 英语文字: public 文字{
public:
英语文字();
};
// 产品等级结构2
class 语音{
public:
语音();
}
class 汉语语音: public 语音{
public:
汉语语音();
};
class 英语语音: public 语音{
public:
英语语音();
};
////高层模块调用//////////////////////////////////////
...
国际化 *language = new 汉语();
文字 *text = language->文字();
语音 *voice = language->语音();
...
2)点评
说点自己的浅见,像这样产品族与产品结构的关系,直接用一个二维数组不就解决了嘛?干嘛还要用类一个一个分隔开来?再一想不对啊,如果这是一个二维数组,那么每一个数组元素就是一个处理单元啊,是一个具体实现啊。也就是说,当产品结构中的元素不仅仅含有结构,还包含操作步骤的时候,这个粒度就不是单单使用数据结构所能驾驭的了。
3)评级
游戏中使用频度评级:★★★☆☆
三、参考
- 抽象工厂模式(引用了其中对产品族与产品等级结构的讲解图片,貌似是译作)
- 设计模式:03-抽象工厂(AbstractFactory)(以《忍者跳跃》为例子介绍抽象工厂模式在游戏多场景主题时的应用。)
- 游戏中的设计模式之抽象工厂模式(为抽象工厂模式在游戏中的应用提供了思路,如果参数化创建怪物可以用来方便地进行类似于梦幻或者山口山的组队)
- C++设计模式——抽象工厂模式(文章排版挺不错,我喜欢。对抽象工厂模式进行了介绍)
- 星际争霸之php抽象工厂模式(游戏敌我阵营作为产品族使用抽象工厂模式的例子)
- 探索设计模式之三——抽象工厂模式(将星际争霸中主力、维护和空军兵种的选择作为一个产品族,以此介绍抽象工厂模式。此介绍可用于电脑出兵策略控制。)
- 【干货】设计模式--抽象工厂模式在unity3d里面的使用(介绍了抽象工厂模式在资源管理器中的应用。附上了实际可用的代码。)
- AD,ADC,AP,APC,DPS,CARRY到底都是啥?(关于英雄联盟中一些常见词汇的解释)