Abstract factory pattern - principle to practical application (based on Dart language)

Object Oriented Design Patterns
Abstract Factory Pattern - Principle to Practical Application (Dart Edition)

- Article information -
Author: 李俊才 (jcLee95)
Visit me at: https://jclee95.blog.csdn.net
Email: [email protected].
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/131731937

【介绍】:本文介绍抽象工厂模式原理及其应用。

Tip: It is recommended to read and master the factory mode before reading this article : https://blog.csdn.net/qq_28550263/article/details/131729619

Previous section: " Factory Mode - Principle to Practical Application (Dart Edition) " | Next section: " Builder Mode - Principle to Practical Application (Dart Edition) "


1. References

In the previous blog post, we gave an example of creating a "game character" for the "factory pattern". Next, it is still assumed that we have a character system in game development. The difference is that now we not only need to create different types of characters , but also create the corresponding equipment for the characters . The categories of characters include Warrior (warrior), Mage (mage), Rogue (rogue); equipment categories include Weapons (weapons), Gloves (gloves), Chestplate (cuirass), Boots (boots), Ring (cutoff), Relic (magic weapon).

Now if we directly separate the characters and equipment, we need to create all the character classes and all the equipment classes first, then combine the characters and equipment, and use keywords to create specific characters and specific equipment. Such codes are newobviously will be difficult to maintain.

So can the factory model introduced in the previous blog post complete this character?

Creator
+createProduct()
ConcreteCreatorA
+createProduct()
ConcreteCreatorB
+createProduct()
Product
+operation()
ConcreteProductA
+operation()
ConcreteProductB
+operation()
Client
Figure 1: Factory pattern structure diagram

From Figure 1, we can see that there are two abstract classes or interfaces in the factory pattern that represent the abstract factory (Creator) and the abstract product (Product), in fact, a specific factory (such as ConcreteCreatorA) produces a corresponding image The specific product (such as ConcreteProductA), the whole process only involves the production of the same type of product . For example, in the previous example, no matter which specific character class belongs to the same class Character , each specific character class is created by a corresponding character factory.

Obviously, since the specific products in this example belong to two categories, the factory model is invalid.

2. What is the abstract factory pattern

2.1 Principle of Abstract Factory Pattern

The Abstract Factory pattern is a creational design pattern that provides a way to create a set of related or interdependent objects without specifying their concrete classes. This pattern separates the creation and use of objects by introducing abstract factories and abstract product classes, thereby achieving a higher level of decoupling. The same as the factory pattern, in the abstract factory pattern, there are also the following four core components:

components describe
Abstract Factory (AbstractFactory) Defines an interface for creating a set of related products. It usually contains multiple factory methods, each method is used to create a concrete product.
Concrete Factory (ConcreteFactory) The abstract factory interface is implemented and is responsible for creating instances of specific products. Each concrete plant corresponds to a product family, a group of related products.
Abstract Product (AbstractProduct) The interface of the product is defined, and the common attributes and methods of the product are described.
Specific products (ConcreteProduct) The abstract product interface is realized, and the specific product corresponds to the specific factory, representing a specific product.

The structural relationship between the various components of the abstract factory pattern can be represented by UML as shown in Figure 2.

实现或继承
实现或继承
创建
创建
创建
创建
实现或继承
实现或继承
实现或继承
实现或继承
AbstractFactory
+FactoryMethod()
ConcreteFactory1
+createProductA()
+createProductB()
+FactoryMethod()
ConcreteFactory2
+createProductA()
+createProductB()
+FactoryMethod()
AbstractProductA
+usefulFunctionA()
ConcreteProductA1
+usefulFunctionA()
ConcreteProductA2
+usefulFunctionA()
AbstractProductB
+usefulFunctionB()
+anotherUsefulFunctionB()
ConcreteProductB1
+usefulFunctionB()
+anotherUsefulFunctionB()
ConcreteProductB2
+usefulFunctionB()
+anotherUsefulFunctionB()
Figure 2: Abstract factory pattern structure diagram

2.2 Differences from factory mode

Abstract Factory Pattern and Factory Pattern are two common design patterns that have some similarities but also some differences.

  1. The purpose and scope of use are different:

    • The factory pattern (Factory Pattern) aims to realize the instantiation of objects by encapsulating the creation process of objects, so that objects can be dynamically created at runtime according to client needs.
    • 抽象工厂模式(Abstract Factory Pattern)则提供一种方式来创建一系列相关的对象,而无需按照具体的类进行实例化。
  2. 关注点不同:

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

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

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

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

3.1 人物角色

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

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

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

Character
+blood: double
+mana: double
+grade: int
+display()
Warrior
+blood: double
+mana: double
+grade: int
+display()
Mage
+blood: double
+mana: double
+grade: int
+display()
Rogue
+blood: double
+mana: double
+grade: int
+display()
图 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;
  }

  
  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;
  }

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

3.2 角色装备

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

Equipment
+wear: double
+display()
Weapons
+wear: double
+display()
Chestplate
+wear: double
+display()
Boots
+wear: double
+display()
Relic
+wear: double
+display()
Gloves
+wear: double
+display()
Ring
+wear: double
+display()
图 4: 装备结构图

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3.3 战士工厂

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

WarriorFactory
+createCharacter()
+createEquipments()
Warrior
+blood: double
+mana: double
+grade: int
+display()
Weapons
+wear: double
+display()
Chestplate
+wear: double
+display()
Boots
+wear: double
+display()
图 5: 战士工厂结构图

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

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

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

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

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

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

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

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

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

3.4 法师工厂

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

MageFactory
+createCharacter()
+createEquipments()
Mage
+blood: double
+mana: double
+grade: int
+display()
Boots
+wear: double
+display()
Relic
+wear: double
+display()
Ring
+wear: double
+display()
图 6: 法师工厂结构图

对应于 UML 的代码实现为:

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

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

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

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

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

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

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

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

3.5 盗贼工厂

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

RogueFactory
+createCharacter()
+createEquipments()
Rogue
+blood: double
+mana: double
+grade: int
+display()
Gloves
+wear: double
+display()
Boots
+wear: double
+display()
图 7: 盗贼工厂结构图

对应于 UML 的代码实现为:

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

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

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

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

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

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

3.6 游戏工厂

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

GameFactory
+createCharacter()
+createEquipments()
WarriorFactory
+createCharacter()
+createEquipments()
MageFactory
+createCharacter()
+createEquipments()
RogueFactory
+createCharacter()
+createEquipments()
图 8:游戏工厂结构图

对应的代码实现为:

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

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

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

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

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

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

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

3.7 整体实现

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

Character
+blood: double
+mana: double
+grade: int
+display()
Warrior
+blood: double
+mana: double
+grade: int
+display()
Mage
+blood: double
+mana: double
+grade: int
+display()
Rogue
+blood: double
+mana: double
+grade: int
+display()
Equipment
+wear: double
+display()
Weapons
+wear: double
+display()
Chestplate
+wear: double
+display()
Boots
+wear: double
+display()
Relic
+wear: double
+display()
Gloves
+wear: double
+display()
Ring
+wear: double
+display()
GameFactory
+createCharacter()
+createEquipments()
WarriorFactory
+createCharacter()
+createEquipments()
MageFactory
+createCharacter()
+createEquipments()
RogueFactory
+createCharacter()
+createEquipments()
图 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;
  }

  
  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;
  }

  
  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;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  
  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

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

Guess you like

Origin blog.csdn.net/qq_28550263/article/details/131731937