抽象工厂模式-原理到实战应用(基于Dart语言)

简介: 抽象工厂模式-原理到实战应用(基于Dart语言)

面向对象之设计模式      抽象工厂模式-原理到实战应用(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 关键字创建具体的角色和具体的装备,这样的代码显然将很难维护。

那么上一篇博文介绍的工厂模式可以完成这次的人物吗?

chrome_VqSOnpzgT2.png

图 1: 工厂模式结构图

从 图1 中我们可以看出,工厂模式中有两个抽象类或接口分别表示 抽象工厂(Creator)和抽象产品(Product),实际上由某个 具体工厂(如ConcreteCreatorA)来生产某个像对应的具体产品(如ConcreteProductA),整个过程仅仅涉及同一类产品的生产。比如在上一个例子中,不论哪个具体的角色类都属于同一个大类 Character,每一个具体的角色类由一个对于的角色工厂进行创建。

很显然,由于这个例子中具体产品分属于两个大类,工厂模式失效了。

2. 什么是抽象工厂模式

2.1 抽象工厂模式原理

抽象工厂模式是一种创建型设计模式,它提供了一种方法来创建一组相关或相互依赖的对象,而无需指定其具体类。该模式通过引入抽象工厂和抽象产品类,将对象的创建和使用分离开来,从而实现了更高层次的解耦。与工厂模式相同的是,在抽象工厂模式中,也有以下四个核心组件:

组件 描述
抽象工厂(AbstractFactory) 定义了用于创建一组相关产品的接口。它通常包含多个工厂方法,每个方法用于创建一个具体产品。
具体工厂(ConcreteFactory) 实现了抽象工厂接口,负责创建具体产品的实例。每个具体工厂对应于一个产品族,即一组相关的产品。
抽象产品(AbstractProduct) 定义了产品的接口,描述了产品的共同属性和方法。
具体产品(ConcreteProduct) 实现了抽象产品接口,具体产品与具体工厂相对应,代表了某个具体的产品。

抽象工厂模式各个组件之间的结构关系可由如 图2 UML所表示。


chrome_5vxVxbyGT2.png

图 2: 抽象工厂模式结构图

2.2 与工厂模式的不同

抽象工厂模式和工厂模式是两种常见的设计模式,它们有一些相似之处,但也有一些区别。

  1. 目的和使用范围不同:
  • 工厂模式(Factory Pattern)旨在通过封装对象的创建过程来实现对象的实例化,以便在运行时根据客户端需求动态创建对象。
  • 抽象工厂模式(Abstract Factory Pattern)则提供一种方式来创建一系列相关的对象,而无需按照具体的类进行实例化。
  1. 关注点不同:
  • 工厂模式关注于创建对象,它提供了一个独立的工厂类来负责创建产品。
  • 抽象工厂模式关注于创建一系列相关的产品,它使用抽象工厂接口来定义这些产品,并由具体工厂类来实现创建过程。
  1. 扩展性不同:
  • 工厂模式相对较简单,通过添加新的具体工厂类和产品类来扩展已有代码。
  • 抽象工厂模式更加灵活,通过添加新的抽象产品类和具体工厂类来扩展已有代码。

总之,工厂模式适用于需要根据不同的实例化需求创建对象的情况,而抽象工厂模式适用于需要创建一系列相关产品的情况。根据具体的需求和设计目标,选择适合的设计模式可以提高代码的可维护性和扩展性。

3. 实战:通过抽象工厂模式建模并给出代码

3.1 人物角色

现在我们回过头来思考本文开篇提出的例子。在我们开发一款游戏时,不仅需要创建不同种类的人物角色,还要创建角色对应的装备。其中角色的类别包括 Warrior(战士)、Mage(法师)、Rogue(盗贼);装备类别包括 Weapons(武器)、Gloves(手套)、Chestplate(胸甲)、Boots(靴子)、Ring(截止)、Relic(法宝)。

因此整体上我们要创建的时带装备的角色,角色和装备都应该各自依据某种规则被创建。因此不妨将角色和装备视作 具体产品,它们由抽象的人物角色和抽象的装备所泛化。

其中有关于人物角色的结构如图3所示:

chrome_SlObQ8OFG9.png

图 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;
  }
@overridevoiddisplay() {
print("战士:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}
// 具体产品:法师classMageimplementsCharacter {
latedoubleblood;
latedoublemana;
lateintgrade;
Mage([doubleblood=100, doublemana=100, intgrade=1]) {
this.blood=blood;
this.mana=mana;
this.grade=grade;
  }
@overridevoiddisplay() {
print("法师:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

3.2 角色装备

其中有关于角色装备的结构如图4所示:

chrome_xcebFIV3N5.png

图 4: 装备结构图

可以依据 UML 完成这部分对应的代码:

// 抽象产品:装备abstractclassEquipment {
doublewear=100.0; // 耐久度voiddisplay(); // 显示装备信息}
// 具体产品:武器classWeaponsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("武器:耐久度=$wear");
  }
}
// 具体产品:胸甲classChestplateimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("胸甲:耐久度=$wear");
  }
}
// 具体产品:鞋子classBootsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("鞋子:耐久度=$wear");
  }
}
// 具体产品:法器classRelicimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("法器:耐久度=$wear");
  }
}
// 具体产品:手套classGlovesimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("手套:耐久度=$wear");
  }
}
// 具体产品:指环classRingimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("指环:耐久度=$wear");
  }
}

3.3 战士工厂

现在我们需要考虑,有一些工厂,不仅仅可以生产人物角色,同时生产对应的装备。考虑到不同角色的装备不同,这实际上就是说一个工厂内需要同时生产人物角色及其对应的装备。例如有一个战士工厂,它不仅仅需要生产战士,还需要生产战士的武器、胸甲、鞋子这些装备。依据此可以绘制战士工厂的 UML 图,如图5所示:

chrome_sHrMwgTqnm.png

图 5: 战士工厂结构图

可以依据 UML 相应地写出这部分代码:

// 具体产品:武器classWeaponsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("武器:耐久度=$wear");
  }
}
// 具体产品:胸甲classChestplateimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("胸甲:耐久度=$wear");
  }
}
// 具体产品:鞋子classBootsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("鞋子:耐久度=$wear");
  }
}
// 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnWarrior();
  }
@overrideList<Equipment>createEquipments() {
// 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}

3.4 法师工厂

同理,我们可以绘制法师工厂的结构图,如图6所示:

chrome_sHrMwgTqnm.png

图 6: 法师工厂结构图

对应于 UML 的代码实现为:

// 具体工厂:法师工厂classMageFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnMage();
  }
@overrideList<Equipment>createEquipments() {
// 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}
// 具体产品:鞋子classBootsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("鞋子:耐久度=$wear");
  }
}
// 具体产品:法器classRelicimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("法器:耐久度=$wear");
  }
}
// 具体产品:指环classRingimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("指环:耐久度=$wear");
  }
}

3.5 盗贼工厂

同理,我们可以绘制盗贼工厂的结构图,如图7所示:

chrome_XpAiMI337n.png

图 7: 盗贼工厂结构图

对应于 UML 的代码实现为:

// 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnRogue();
  }
@overrideList<Equipment>createEquipments() {
// 盗贼自带鞋子和手套装备returnList<Equipment>.from([Boots(), Gloves()]);
  }
}
// 具体产品:手套classGlovesimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("手套:耐久度=$wear");
  }
}
// 具体产品:鞋子classBootsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("鞋子:耐久度=$wear");
  }
}

3.6 游戏工厂

从最顶层来看,游戏工厂提供统一的接口,是一个抽象工厂。而用户混合生产各种具体产的的工厂,如 “战士工厂”、“法师工厂”、“盗贼工厂” 都是基础或者实现了该工厂工厂。这部分的 结构如图8所示:


chrome_mdbPnRMFq7.png

chrome_mdbPnRMFq7.png

图 8:游戏工厂结构图

对应的代码实现为:

// 抽象工厂abstractclassGameFactory {
CharactercreateCharacter(); // 创建角色List<Equipment>createEquipments(); // 创建装备}
// 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnWarrior();
  }
@overrideList<Equipment>createEquipments() {
// 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}
// 具体工厂:法师工厂classMageFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnMage();
  }
@overrideList<Equipment>createEquipments() {
// 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}
// 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnRogue();
  }
@overrideList<Equipment>createEquipments() {
// 盗贼自带鞋子和手套装备returnList<Equipment>.from([Boots(), Gloves()]);
  }
}

3.7 整体实现

整体上,可以绘制出完整的 UML 类图,如图9所示:

chrome_VMrkfF8lJM.png

图 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;
  }
@overridevoiddisplay() {
print("战士:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}
// 具体产品:法师classMageimplementsCharacter {
latedoubleblood;
latedoublemana;
lateintgrade;
Mage([doubleblood=100, doublemana=100, intgrade=1]) {
this.blood=blood;
this.mana=mana;
this.grade=grade;
  }
@overridevoiddisplay() {
print("法师:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}
// 具体产品:盗贼classRogueimplementsCharacter {
latedoubleblood;
latedoublemana;
lateintgrade;
Rogue([doubleblood=100, doublemana=0, intgrade=1]) {
this.blood=blood;
this.mana=mana;
this.grade=grade;
  }
@overridevoiddisplay() {
print("盗贼:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}
// 抽象产品:装备abstractclassEquipment {
doublewear=100.0; // 耐久度voiddisplay(); // 显示装备信息}
// 具体产品:武器classWeaponsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("武器:耐久度=$wear");
  }
}
// 具体产品:胸甲classChestplateimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("胸甲:耐久度=$wear");
  }
}
// 具体产品:鞋子classBootsimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("鞋子:耐久度=$wear");
  }
}
// 具体产品:法器classRelicimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("法器:耐久度=$wear");
  }
}
// 具体产品:手套classGlovesimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("手套:耐久度=$wear");
  }
}
// 具体产品:指环classRingimplementsEquipment {
doublewear=100.0;
@overridevoiddisplay() {
print("指环:耐久度=$wear");
  }
}
// 抽象工厂abstractclassGameFactory {
CharactercreateCharacter(); // 创建角色List<Equipment>createEquipments(); // 创建装备}
// 具体工厂:战士工厂classWarriorFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnWarrior();
  }
@overrideList<Equipment>createEquipments() {
// 战士自带武器、鞋子、胸甲装备returnList<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}
// 具体工厂:法师工厂classMageFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnMage();
  }
@overrideList<Equipment>createEquipments() {
// 法师自带鞋子、法器、指环装备returnList<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}
// 具体工厂:盗贼工厂classRogueFactoryimplementsGameFactory {
@overrideCharactercreateCharacter() {
returnRogue();
  }
@overrideList<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

可以看到,战士工厂、法师工厂、盗贼工厂,不仅仅可以生产自己需要的角色(依次对应战士、法师、盗贼),同时可以生产对应于自己角色的装备。如战士的装备为武器、胸甲、鞋子;法师的装备为法器、鞋子、指环;盗贼的装备为鞋子、手套。这就是我们所说的抽象工厂模式再实际开发的一个应用的例子。

目录
相关文章
|
iOS开发 开发者
【教程】苹果 iOS 证书制作教程
【教程】苹果 iOS 证书制作教程
|
JavaScript 前端开发 API
什么是声明式UI什么是命令式UI?鸿蒙ArkTS为什么是声明式UI-优雅草卓伊凡
什么是声明式UI什么是命令式UI?鸿蒙ArkTS为什么是声明式UI-优雅草卓伊凡
382 12
什么是声明式UI什么是命令式UI?鸿蒙ArkTS为什么是声明式UI-优雅草卓伊凡
|
8月前
|
存储 安全 开发工具
如何安全删除GitHub中的敏感文件?git-filter-repo操作全解析
当敏感文件误传至GitHub时,需使用`git-filter-repo`彻底删除文件及历史记录。本文详解操作步骤与注意事项,如备份、强制推送、团队协作处理,并建议搭配高安全性云服务,防止数据泄露,保障代码仓库安全。
713 1
|
存储 自然语言处理 供应链
跨境电商团队如何高效管理项目?这5款协作工具值得尝试
跨境电商团队面临的全球化供应链、跨文化沟通、时区差异及语言障碍等挑战,可通过选择合适的协作工具来克服。推荐工具包括板栗看板、Trello、Slack、Asana和Zoom,它们分别在任务管理、即时通讯、项目跟踪和视频会议等方面提供强大支持,帮助团队提升效率、确保任务高效执行和顺畅沟通。
跨境电商团队如何高效管理项目?这5款协作工具值得尝试
|
机器学习/深度学习 人工智能 JSON
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
Resume Matcher 是一款开源AI简历优化工具,通过解析简历和职位描述,提取关键词并计算文本相似性,帮助求职者优化简历内容,提升通过自动化筛选系统(ATS)的概率,增加面试机会。
1686 18
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
|
11月前
|
人工智能 自然语言处理 JavaScript
【开源项目】MaxKB4J基于java开发的工作流和 RAG智能体的知识库问答系统
MaxKB4J是一款基于Java开发的开源LLM工作流应用与RAG知识库问答系统,结合MaxKB和FastGPT优势,支持智能客服、企业知识库等场景。它开箱即用,可直接上传/爬取文档,支持多种大模型(如Qwen、通义千问等),具备灵活的工作流编排能力,并无缝嵌入第三方系统。技术栈包括Vue.js、Springboot3、PostgreSQL等,提供稳定高效的智能问答解决方案。访问地址:`http://localhost:8080/ui/login`,项目详情见[Gitee](https://gitee.com/taisan/MaxKB4j)。
|
存储 Dart Java
Dart 虚拟机运行原理
【10月更文挑战第20天】Dart 虚拟机通过一系列复杂的机制和操作,确保 Dart 代码能够准确、高效地执行。它为 Dart 语言的广泛应用提供了坚实的基础和可靠的运行环境
459 6
Flutter 自定义组件继承与调用的高级使用方式
本文深入探讨了 Flutter 中自定义组件的高级使用方式,包括创建基本自定义组件、继承现有组件、使用 Mixins 和组合模式等。通过这些方法,您可以构建灵活、可重用且易于维护的 UI 组件,从而提升开发效率和代码质量。
513 1
|
存储 网络安全 对象存储
缺乏中间证书导致通过HTTPS协议访问OSS异常
【10月更文挑战第4天】缺乏中间证书导致通过HTTPS协议访问OSS异常
1328 4
|
算法 程序员 Linux
使用国密版cURL访问国密https网站
国密版cURL使用指南
3357 0
使用国密版cURL访问国密https网站