抽象工厂模式-原理到实战应用(基于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

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

目录
相关文章
|
Dart JavaScript 前端开发
带你读《深入浅出Dart》七、类和对象(1)
带你读《深入浅出Dart》七、类和对象(1)
带你读《深入浅出Dart》十一、Dart模块化详解
带你读《深入浅出Dart》十一、Dart模块化详解
|
4月前
|
程序员 Go
Go 语言:面向对象还是非面向对象?揭开编程语言的本质
Go 语言:面向对象还是非面向对象?揭开编程语言的本质
|
7月前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
87 1
|
7月前
|
设计模式 Java
23种设计模式,建造者模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】建造者模式是一种对象创建型设计模式,用于创建复杂对象。其核心思想是将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常包括一个Director类,用于管理构建过程,和多个Builder类,每个Builder负责构造复杂对象的一部分。
94 7
带你读《深入浅出Dart》七、类和对象(2)
带你读《深入浅出Dart》七、类和对象(2)
|
7月前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
7月前
|
设计模式 算法 Java
23种设计模式,模板方法模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】模板方法模式是一种行为设计模式,它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
68 0
|
存储 Dart 安全
带你读《深入浅出Dart》十九、Dart中泛型
带你读《深入浅出Dart》十九、Dart中泛型