抽象工厂模式-原理到实战应用(Dart版)

举报
jcLee95 发表于 2023/07/16 19:00:07 2023/07/16
【摘要】 抽象工厂模式-原理到实战应用(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



在上一篇博文中,我们为“工厂模式”给出了一个创建“游戏角色”的例子。接下来仍然假设我们有一个游戏开发中的角色系统,不同的是现在我们不仅需要创建不同种类的人物角色,还要创建角色对应的装备。其中角色的类别包括 Warrior(战士)、Mage(法师)、Rogue(盗贼);装备类别包括 Weapons(武器)、Gloves(手套)、Chestplate(胸甲)、Boots(靴子)、Ring(截止)、Relic(法宝)。

现在我们如果直接分别对人物角色和装备,则需要先创建所有的角色类和所有的装备类,然后对角色和装备进行组合,使用通过 new 关键字创建具体的角色和具体的装备,这样的代码显然将很难维护。

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



图 1: 工厂模式结构图

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

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



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

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

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

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

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

  1. 目的和使用范围不同:

    • 工厂模式(Factory Pattern)旨在通过封装对象的创建过程来实现对象的实例化,以便在运行时根据客户端需求动态创建对象。
    • 抽象工厂模式(Abstract Factory Pattern)则提供一种方式来创建一系列相关的对象,而无需按照具体的类进行实例化。
  2. 关注点不同:

    • 工厂模式关注于创建对象,它提供了一个独立的工厂类来负责创建产品。
    • 抽象工厂模式关注于创建一系列相关的产品,它使用抽象工厂接口来定义这些产品,并由具体工厂类来实现创建过程。
  3. 扩展性不同:

    • 工厂模式相对较简单,通过添加新的具体工厂类和产品类来扩展已有代码。
    • 抽象工厂模式更加灵活,通过添加新的抽象产品类和具体工厂类来扩展已有代码。

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



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

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

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



图 3: 任务角色结构图

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

// 抽象产品:角色
abstract class Character {
  late double blood; // 血量
  late double mana; // 法力值
  late int grade; // 等级

  void display(); // 显示角色信息
}

// 具体产品:战士
class Warrior implements Character {
  late double blood;
  late double mana;
  late int grade;

  Warrior([double blood = 200, double mana = 0, int grade = 1]) {
    this.blood = blood;
    this.mana = mana;
    this.grade = grade;
  }

  @override
  void display() {
    print("战士:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

// 具体产品:法师
class Mage implements Character {
  late double blood;
  late double mana;
  late int grade;

  Mage([double blood = 100, double mana = 100, int grade = 1]) {
    this.blood = blood;
    this.mana = mana;
    this.grade = grade;
  }

  @override
  void display() {
    print("法师:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

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




图 4: 装备结构图

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

// 抽象产品:装备
abstract class Equipment {
  double wear = 100.0; // 耐久度

  void display(); // 显示装备信息
}

// 具体产品:武器
class Weapons implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("武器:耐久度=$wear");
  }
}

// 具体产品:胸甲
class Chestplate implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("胸甲:耐久度=$wear");
  }
}

// 具体产品:鞋子
class Boots implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("鞋子:耐久度=$wear");
  }
}

// 具体产品:法器
class Relic implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("法器:耐久度=$wear");
  }
}

// 具体产品:手套
class Gloves implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("手套:耐久度=$wear");
  }
}

// 具体产品:指环
class Ring implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("指环:耐久度=$wear");
  }
}

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




图 5: 战士工厂结构图

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

// 具体产品:武器
class Weapons implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("武器:耐久度=$wear");
  }
}

// 具体产品:胸甲
class Chestplate implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("胸甲:耐久度=$wear");
  }
}

// 具体产品:鞋子
class Boots implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("鞋子:耐久度=$wear");
  }
}

// 具体工厂:战士工厂
class WarriorFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Warrior();
  }

  @override
  List<Equipment> createEquipments() {
    // 战士自带武器、鞋子、胸甲装备
    return List<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}


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



图 6: 法师工厂结构图

对应于 UML 的代码实现为:

// 具体工厂:法师工厂
class MageFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Mage();
  }

  @override
  List<Equipment> createEquipments() {
    // 法师自带鞋子、法器、指环装备
    return List<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}

// 具体产品:鞋子
class Boots implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("鞋子:耐久度=$wear");
  }
}

// 具体产品:法器
class Relic implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("法器:耐久度=$wear");
  }
}

// 具体产品:指环
class Ring implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("指环:耐久度=$wear");
  }
}

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



图 7: 盗贼工厂结构图

对应于 UML 的代码实现为:

// 具体工厂:盗贼工厂
class RogueFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Rogue();
  }

  @override
  List<Equipment> createEquipments() {
    // 盗贼自带鞋子和手套装备
    return List<Equipment>.from([Boots(), Gloves()]);
  }
}

// 具体产品:手套
class Gloves implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("手套:耐久度=$wear");
  }
}

// 具体产品:鞋子
class Boots implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("鞋子:耐久度=$wear");
  }
}

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



图 8:游戏工厂结构图

对应的代码实现为:

// 抽象工厂
abstract class GameFactory {
  Character createCharacter(); // 创建角色
  List<Equipment> createEquipments(); // 创建装备
}

// 具体工厂:战士工厂
class WarriorFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Warrior();
  }

  @override
  List<Equipment> createEquipments() {
    // 战士自带武器、鞋子、胸甲装备
    return List<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}

// 具体工厂:法师工厂
class MageFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Mage();
  }

  @override
  List<Equipment> createEquipments() {
    // 法师自带鞋子、法器、指环装备
    return List<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}

// 具体工厂:盗贼工厂
class RogueFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Rogue();
  }

  @override
  List<Equipment> createEquipments() {
    // 盗贼自带鞋子和手套装备
    return List<Equipment>.from([Boots(), Gloves()]);
  }
}

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



图 9: 整体结构图

通过 UML类图 编写代码,对应的完整代码为:

// 抽象产品:角色
abstract class Character {
  late double blood; // 血量
  late double mana; // 法力值
  late int grade; // 等级

  void display(); // 显示角色信息
}

// 具体产品:战士
class Warrior implements Character {
  late double blood;
  late double mana;
  late int grade;

  Warrior([double blood = 200, double mana = 0, int grade = 1]) {
    this.blood = blood;
    this.mana = mana;
    this.grade = grade;
  }

  @override
  void display() {
    print("战士:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

// 具体产品:法师
class Mage implements Character {
  late double blood;
  late double mana;
  late int grade;

  Mage([double blood = 100, double mana = 100, int grade = 1]) {
    this.blood = blood;
    this.mana = mana;
    this.grade = grade;
  }

  @override
  void display() {
    print("法师:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

// 具体产品:盗贼
class Rogue implements Character {
  late double blood;
  late double mana;
  late int grade;

  Rogue([double blood = 100, double mana = 0, int grade = 1]) {
    this.blood = blood;
    this.mana = mana;
    this.grade = grade;
  }

  @override
  void display() {
    print("盗贼:血量=$blood, 法力值=$mana, 等级=$grade");
  }
}

// 抽象产品:装备
abstract class Equipment {
  double wear = 100.0; // 耐久度

  void display(); // 显示装备信息
}

// 具体产品:武器
class Weapons implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("武器:耐久度=$wear");
  }
}

// 具体产品:胸甲
class Chestplate implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("胸甲:耐久度=$wear");
  }
}

// 具体产品:鞋子
class Boots implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("鞋子:耐久度=$wear");
  }
}

// 具体产品:法器
class Relic implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("法器:耐久度=$wear");
  }
}

// 具体产品:手套
class Gloves implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("手套:耐久度=$wear");
  }
}

// 具体产品:指环
class Ring implements Equipment {
  double wear = 100.0;

  @override
  void display() {
    print("指环:耐久度=$wear");
  }
}

// 抽象工厂
abstract class GameFactory {
  Character createCharacter(); // 创建角色
  List<Equipment> createEquipments(); // 创建装备
}

// 具体工厂:战士工厂
class WarriorFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Warrior();
  }

  @override
  List<Equipment> createEquipments() {
    // 战士自带武器、鞋子、胸甲装备
    return List<Equipment>.from([Boots(), Weapons(), Chestplate()]);
  }
}

// 具体工厂:法师工厂
class MageFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Mage();
  }

  @override
  List<Equipment> createEquipments() {
    // 法师自带鞋子、法器、指环装备
    return List<Equipment>.from([Boots(), Relic(), Ring()]);
  }
}

// 具体工厂:盗贼工厂
class RogueFactory implements GameFactory {
  @override
  Character createCharacter() {
    return Rogue();
  }

  @override
  List<Equipment> createEquipments() {
    // 盗贼自带鞋子和手套装备
    return List<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

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

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。