面向对象之设计模式 抽象工厂模式-原理到实战应用(Dart版)
- 文章信息 -Author:李俊才 (jcLee95)
Visit me at: https://jclee95.blog.csdn.net
Email:291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/131731937
【介绍】:本文介绍抽象工厂模式原理及其应用。
提示:建议阅读本文前先阅读并掌握 工厂模式:https://blog.csdn.net/qq_28550263/article/details/131729619
上一节:《 工厂模式-原理到实战应用(Dart版) 》 | 下一节:《 构建器模式-原理到实战应用(Dart版) 》
目 录
1. 引例
在上一篇博文中,我们为“工厂模式”给出了一个创建“游戏角色”的例子。接下来仍然假设我们有一个游戏开发中的角色系统,不同的是现在我们不仅需要创建不同种类的人物角色,还要创建角色对应的装备。其中角色的类别包括 Warrior(战士)、Mage(法师)、Rogue(盗贼);装备类别包括 Weapons(武器)、Gloves(手套)、Chestplate(胸甲)、Boots(靴子)、Ring(截止)、Relic(法宝)。
现在我们如果直接分别对人物角色和装备,则需要先创建所有的角色类和所有的装备类,然后对角色和装备进行组合,使用通过 new
关键字创建具体的角色和具体的装备,这样的代码显然将很难维护。
那么上一篇博文介绍的工厂模式可以完成这次的人物吗?
图 1: 工厂模式结构图
从 图1 中我们可以看出,工厂模式中有两个抽象类或接口分别表示 抽象工厂(Creator)和抽象产品(Product),实际上由某个 具体工厂(如ConcreteCreatorA)来生产某个像对应的具体产品(如ConcreteProductA),整个过程仅仅涉及同一类产品的生产。比如在上一个例子中,不论哪个具体的角色类都属于同一个大类 Character,每一个具体的角色类由一个对于的角色工厂进行创建。
很显然,由于这个例子中具体产品分属于两个大类,工厂模式失效了。
2. 什么是抽象工厂模式
2.1 抽象工厂模式原理
抽象工厂模式是一种创建型设计模式,它提供了一种方法来创建一组相关或相互依赖的对象,而无需指定其具体类。该模式通过引入抽象工厂和抽象产品类,将对象的创建和使用分离开来,从而实现了更高层次的解耦。与工厂模式相同的是,在抽象工厂模式中,也有以下四个核心组件:
组件 | 描述 |
抽象工厂(AbstractFactory) | 定义了用于创建一组相关产品的接口。它通常包含多个工厂方法,每个方法用于创建一个具体产品。 |
具体工厂(ConcreteFactory) | 实现了抽象工厂接口,负责创建具体产品的实例。每个具体工厂对应于一个产品族,即一组相关的产品。 |
抽象产品(AbstractProduct) | 定义了产品的接口,描述了产品的共同属性和方法。 |
具体产品(ConcreteProduct) | 实现了抽象产品接口,具体产品与具体工厂相对应,代表了某个具体的产品。 |
抽象工厂模式各个组件之间的结构关系可由如 图2 UML所表示。
图 2: 抽象工厂模式结构图
2.2 与工厂模式的不同
抽象工厂模式和工厂模式是两种常见的设计模式,它们有一些相似之处,但也有一些区别。
- 目的和使用范围不同:
- 工厂模式(Factory Pattern)旨在通过封装对象的创建过程来实现对象的实例化,以便在运行时根据客户端需求动态创建对象。
- 抽象工厂模式(Abstract Factory Pattern)则提供一种方式来创建一系列相关的对象,而无需按照具体的类进行实例化。
- 关注点不同:
- 工厂模式关注于创建对象,它提供了一个独立的工厂类来负责创建产品。
- 抽象工厂模式关注于创建一系列相关的产品,它使用抽象工厂接口来定义这些产品,并由具体工厂类来实现创建过程。
- 扩展性不同:
- 工厂模式相对较简单,通过添加新的具体工厂类和产品类来扩展已有代码。
- 抽象工厂模式更加灵活,通过添加新的抽象产品类和具体工厂类来扩展已有代码。
总之,工厂模式适用于需要根据不同的实例化需求创建对象的情况,而抽象工厂模式适用于需要创建一系列相关产品的情况。根据具体的需求和设计目标,选择适合的设计模式可以提高代码的可维护性和扩展性。
3. 实战:通过抽象工厂模式建模并给出代码
3.1 人物角色
现在我们回过头来思考本文开篇提出的例子。在我们开发一款游戏时,不仅需要创建不同种类的人物角色,还要创建角色对应的装备。其中角色的类别包括 Warrior(战士)、Mage(法师)、Rogue(盗贼);装备类别包括 Weapons(武器)、Gloves(手套)、Chestplate(胸甲)、Boots(靴子)、Ring(截止)、Relic(法宝)。
因此整体上我们要创建的时带装备的角色,角色和装备都应该各自依据某种规则被创建。因此不妨将角色和装备视作 具体产品,它们由抽象的人物角色和抽象的装备所泛化。
其中有关于人物角色的结构如图3所示:
图 3: 任务角色结构图
可以依据 UML 完成这部分对应的代码:
// 抽象产品:角色abstractclassCharacter { latedoubleblood; // 血量latedoublemana; // 法力值lateintgrade; // 等级voiddisplay(); // 显示角色信息} // 具体产品:战士classWarriorimplementsCharacter { latedoubleblood; latedoublemana; lateintgrade; Warrior([doubleblood=200, doublemana=0, intgrade=1]) { this.blood=blood; this.mana=mana; this.grade=grade; } voiddisplay() { print("战士:血量=$blood, 法力值=$mana, 等级=$grade"); } } // 具体产品:法师classMageimplementsCharacter { latedoubleblood; latedoublemana; lateintgrade; Mage([doubleblood=100, doublemana=100, intgrade=1]) { this.blood=blood; this.mana=mana; this.grade=grade; } voiddisplay() { print("法师:血量=$blood, 法力值=$mana, 等级=$grade"); } }
3.2 角色装备
其中有关于角色装备的结构如图4所示:
图 4: 装备结构图
可以依据 UML 完成这部分对应的代码:
// 抽象产品:装备abstractclassEquipment { doublewear=100.0; // 耐久度voiddisplay(); // 显示装备信息} // 具体产品:武器classWeaponsimplementsEquipment { doublewear=100.0; voiddisplay() { print("武器:耐久度=$wear"); } } // 具体产品:胸甲classChestplateimplementsEquipment { doublewear=100.0; voiddisplay() { print("胸甲:耐久度=$wear"); } } // 具体产品:鞋子classBootsimplementsEquipment { doublewear=100.0; voiddisplay() { print("鞋子:耐久度=$wear"); } } // 具体产品:法器classRelicimplementsEquipment { doublewear=100.0; voiddisplay() { print("法器:耐久度=$wear"); } } // 具体产品:手套classGlovesimplementsEquipment { doublewear=100.0; voiddisplay() { print("手套:耐久度=$wear"); } } // 具体产品:指环classRingimplementsEquipment { doublewear=100.0; voiddisplay() { print("指环:耐久度=$wear"); } }
3.3 战士工厂
现在我们需要考虑,有一些工厂,不仅仅可以生产人物角色,同时生产对应的装备。考虑到不同角色的装备不同,这实际上就是说一个工厂内需要同时生产人物角色及其对应的装备。例如有一个战士工厂,它不仅仅需要生产战士,还需要生产战士的武器、胸甲、鞋子这些装备。依据此可以绘制战士工厂的 UML 图,如图5所示:
图 5: 战士工厂结构图
可以依据 UML 相应地写出这部分代码:
// 具体产品:武器classWeaponsimplementsEquipment { doublewear=100.0; voiddisplay() { print("武器:耐久度=$wear"); } } // 具体产品:胸甲classChestplateimplementsEquipment { doublewear=100.0; voiddisplay() { print("胸甲:耐久度=$wear"); } } // 具体产品:鞋子classBootsimplementsEquipment { doublewear=100.0; voiddisplay() { print("鞋子:耐久度=$wear"); } } // 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory { CharactercreateCharacter() { returnWarrior(); } List<Equipment>createEquipments() { // 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]); } }
3.4 法师工厂
同理,我们可以绘制法师工厂的结构图,如图6所示:
图 6: 法师工厂结构图
对应于 UML 的代码实现为:
// 具体工厂:法师工厂classMageFactoryimplementsGameFactory { CharactercreateCharacter() { returnMage(); } List<Equipment>createEquipments() { // 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]); } } // 具体产品:鞋子classBootsimplementsEquipment { doublewear=100.0; voiddisplay() { print("鞋子:耐久度=$wear"); } } // 具体产品:法器classRelicimplementsEquipment { doublewear=100.0; voiddisplay() { print("法器:耐久度=$wear"); } } // 具体产品:指环classRingimplementsEquipment { doublewear=100.0; voiddisplay() { print("指环:耐久度=$wear"); } }
3.5 盗贼工厂
同理,我们可以绘制盗贼工厂的结构图,如图7所示:
图 7: 盗贼工厂结构图
对应于 UML 的代码实现为:
// 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory { CharactercreateCharacter() { returnRogue(); } List<Equipment>createEquipments() { // 盗贼自带鞋子和手套装备returnList<Equipment>.from([Boots(), Gloves()]); } } // 具体产品:手套classGlovesimplementsEquipment { doublewear=100.0; voiddisplay() { print("手套:耐久度=$wear"); } } // 具体产品:鞋子classBootsimplementsEquipment { doublewear=100.0; voiddisplay() { print("鞋子:耐久度=$wear"); } }
3.6 游戏工厂
从最顶层来看,游戏工厂提供统一的接口,是一个抽象工厂。而用户混合生产各种具体产的的工厂,如 “战士工厂”、“法师工厂”、“盗贼工厂” 都是基础或者实现了该工厂工厂。这部分的 结构如图8所示:
图 8:游戏工厂结构图
对应的代码实现为:
// 抽象工厂abstractclassGameFactory { CharactercreateCharacter(); // 创建角色List<Equipment>createEquipments(); // 创建装备} // 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory { CharactercreateCharacter() { returnWarrior(); } List<Equipment>createEquipments() { // 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]); } } // 具体工厂:法师工厂classMageFactoryimplementsGameFactory { CharactercreateCharacter() { returnMage(); } List<Equipment>createEquipments() { // 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]); } } // 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory { CharactercreateCharacter() { returnRogue(); } List<Equipment>createEquipments() { // 盗贼自带鞋子和手套装备returnList<Equipment>.from([Boots(), Gloves()]); } }
3.7 整体实现
整体上,可以绘制出完整的 UML 类图,如图9所示:
图 9: 整体结构图
通过 UML类图 编写代码,对应的完整代码为:
// 抽象产品:角色abstractclassCharacter { latedoubleblood; // 血量latedoublemana; // 法力值lateintgrade; // 等级voiddisplay(); // 显示角色信息} // 具体产品:战士classWarriorimplementsCharacter { latedoubleblood; latedoublemana; lateintgrade; Warrior([doubleblood=200, doublemana=0, intgrade=1]) { this.blood=blood; this.mana=mana; this.grade=grade; } voiddisplay() { print("战士:血量=$blood, 法力值=$mana, 等级=$grade"); } } // 具体产品:法师classMageimplementsCharacter { latedoubleblood; latedoublemana; lateintgrade; Mage([doubleblood=100, doublemana=100, intgrade=1]) { this.blood=blood; this.mana=mana; this.grade=grade; } voiddisplay() { print("法师:血量=$blood, 法力值=$mana, 等级=$grade"); } } // 具体产品:盗贼classRogueimplementsCharacter { latedoubleblood; latedoublemana; lateintgrade; Rogue([doubleblood=100, doublemana=0, intgrade=1]) { this.blood=blood; this.mana=mana; this.grade=grade; } voiddisplay() { print("盗贼:血量=$blood, 法力值=$mana, 等级=$grade"); } } // 抽象产品:装备abstractclassEquipment { doublewear=100.0; // 耐久度voiddisplay(); // 显示装备信息} // 具体产品:武器classWeaponsimplementsEquipment { doublewear=100.0; voiddisplay() { print("武器:耐久度=$wear"); } } // 具体产品:胸甲classChestplateimplementsEquipment { doublewear=100.0; voiddisplay() { print("胸甲:耐久度=$wear"); } } // 具体产品:鞋子classBootsimplementsEquipment { doublewear=100.0; voiddisplay() { print("鞋子:耐久度=$wear"); } } // 具体产品:法器classRelicimplementsEquipment { doublewear=100.0; voiddisplay() { print("法器:耐久度=$wear"); } } // 具体产品:手套classGlovesimplementsEquipment { doublewear=100.0; voiddisplay() { print("手套:耐久度=$wear"); } } // 具体产品:指环classRingimplementsEquipment { doublewear=100.0; voiddisplay() { print("指环:耐久度=$wear"); } } // 抽象工厂abstractclassGameFactory { CharactercreateCharacter(); // 创建角色List<Equipment>createEquipments(); // 创建装备} // 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory { CharactercreateCharacter() { returnWarrior(); } List<Equipment>createEquipments() { // 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]); } } // 具体工厂:法师工厂classMageFactoryimplementsGameFactory { CharactercreateCharacter() { returnMage(); } List<Equipment>createEquipments() { // 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]); } } // 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory { CharactercreateCharacter() { returnRogue(); } List<Equipment>createEquipments() { // 盗贼自带鞋子和手套装备returnList<Equipment>.from([Boots(), Gloves()]); } }
我们可以在一个主函数中调用上面的代码进行简单地测试:
void main() { // 使用战士工厂创建战士角色和武器装备 GameFactory warriorFactory = WarriorFactory(); Character warrior = warriorFactory.createCharacter(); List<Equipment> warriorEquipments = warriorFactory.createEquipments(); // 使用法师工厂创建法师角色和法器装备 GameFactory mageFactory = MageFactory(); Character mage = mageFactory.createCharacter(); List<Equipment> mageEquipments = mageFactory.createEquipments(); // 使用盗贼工厂创建盗贼角色和手套装备 GameFactory rogueFactory = RogueFactory(); Character rogue = rogueFactory.createCharacter(); List<Equipment> rogueEquipments = rogueFactory.createEquipments(); print('-----------------------------'); // 显示角色和装备信息 warrior.display(); warriorEquipments.forEach((element) { element.display(); }); print('-----------------------------'); mage.display(); mageEquipments.forEach((element) { element.display(); }); print('-----------------------------'); rogue.display(); rogueEquipments.forEach((element) { element.display(); }); }
输出结果为:
----------------------------- 战士:血量=200.0, 法力值=0.0, 等级=1 鞋子:耐久度=100.0 武器:耐久度=100.0 胸甲:耐久度=100.0 ----------------------------- 法师:血量=100.0, 法力值=100.0, 等级=1 鞋子:耐久度=100.0 法器:耐久度=100.0 指环:耐久度=100.0 ----------------------------- 盗贼:血量=100.0, 法力值=0.0, 等级=1 鞋子:耐久度=100.0 手套:耐久度=100.0
可以看到,战士工厂、法师工厂、盗贼工厂,不仅仅可以生产自己需要的角色(依次对应战士、法师、盗贼),同时可以生产对应于自己角色的装备。如战士的装备为武器、胸甲、鞋子;法师的装备为法器、鞋子、指环;盗贼的装备为鞋子、手套。这就是我们所说的抽象工厂模式再实际开发的一个应用的例子。