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

面向对象之设计模式
工厂模式-原理到实战应用(Dart版)

- 文章信息 -
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/131729619

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

下一节:《 抽象工厂模式


1. 引例

1.1 创建游戏的角色

在讲解什么是工厂模式前,先请各位读者思考一个问题:假设我们正在开发一个游戏,其中有多种角色可以选择,每个角色都有自己独特的属性和技能,应该如何创建不同类型的角色对象呢?

假设这个游戏包含“战士”、“法师”、“盗贼”、“猎人”、“圣骑士” 这五种人物角色,现在我们通过手动创建每个角色类的方式来表示不同角色,可以使用 Dart 语言实现如下:

// 人物角色类
class Character {
    
    
  String name;
  int level;
  
  Character(this.name, this.level);
  
  void introduce() {
    
    
    print("我是$name,等级$level");
  }
}

// 战士角色
class Warrior extends Character {
    
    
  int strength;
  
  Warrior(String name, int level, this.strength) : super(name, level);
  
  void attack() {
    
    
    print("发动近身攻击!");
  }
}

// 法师角色
class Mage extends Character {
    
    
  int mana;
  
  Mage(String name, int level, this.mana) : super(name, level);
  
  void castSpell() {
    
    
    print("释放魔法攻击!");
  }
}

// 盗贼角色
class Rogue extends Character {
    
    
  int agility;
  
  Rogue(String name, int level, this.agility) : super(name, level);
  
  void stealth() {
    
    
    print("进入潜行状态!");
  }
}

// 猎人角色
class Hunter extends Character {
    
    
  int precision;
  
  Hunter(String name, int level, this.precision) : super(name, level);
  
  void shoot() {
    
    
    print("进行远程射击!");
  }
}

// 圣骑士角色
class Paladin extends Character {
    
    
  int faith;
  
  Paladin(String name, int level, this.faith) : super(name, level);
  
  void smite() {
    
    
    print("发动神圣打击!");
  }
}

这段代码我们先实现了一个 Character 类,用于表示各种人物角色。其中所有教授都应该有自己的 name(名字) 和 level(等级) 这两个属性。为了让每个角色能够介绍自己,我们还增加了一个 introduce 方法。当一种具体的角色类继承于 Character 时,便自动获得了 namelevel 属性以及 introduce 方法。像对应的 UML 类图如图所示:

Character
+name : String
+level : int
+introduce()
Warrior
+strength : int
+attack()
Mage
+mana : int
+castSpell()
Rogue
+agility : int
+stealth()
Hunter
+precision : int
+shoot()
Paladin
+faith : int
+smite()
图 1: 人物角色

1.2 分析与改进

在上面的实现方式中,我们为每一个角色从 Character 泛化出一个新的类,当需要一个角色时通过关键字 new 即可获取一个具体的角色。可以看出,我们会直接依赖于具体的类进行对象的创建,比如通过 Warrior 类来创建新的战士,通过 Mage 类来创建法师等等。这种实现方式的问题在于,如果我们需要更改对象的实现方式。例如我们要对战士类添加一个表示国别的参数用做构造参数,那么类被修改为:

class Warrior extends Character {
    
    
  int strength;
  String country;
  
  Warrior(String name, int level, this.strength, this.country) : super(name, level);
  
  void attack() {
    
    
    print("发动近身攻击!");
  }
}

这将使得我们不得不从整个代码来修改所有使用 new 构造 Warrior 的地方。实际上,通过大量开发者的反复实践,已经归纳出这种直接依赖于具体的类进行对象的创建的方法往往存在以下缺陷:

  1. 代码可读性差:在当前实现方式中,创建对象的代码分散在多个地方,并且可能涉及到多行代码。这样会导致代码很难阅读和维护。使用工厂方法可以将对象的创建过程封装在一个类中,使代码更加整洁和易于理解。
  2. 代码重复:从上面的代码中可以看出,创建对象的流程是相似的,常常包含了重复的代码。通过工厂模式,可以利用封装性将相邻的代码收集在一个类里,防止代码重复。
  3. 缺少灵活性:当前的实现方式使得我们只能在代码中使用 new 来创建对象。如果我们需要更改对象的实现方式,就需要遍历整个代码库来修改所有使用new的地方。当我们在对象创建时使用工厂模式,我们只需修改工厂类的实现即可,而不需要改变调用代码。
  4. 可测试性差:在当前实现方式下,我们无法为对象创建和销毁编写单元测试。如果我们在对象创建时使用工厂方法,我们可以编写单元测试和效用来证明对象的创建和销毁,以及返回的对象的值是否符合预期。

为了避免这些缺陷,工厂模式 应运而生。在接下来的章节中,我们将具体讲解 工厂模式 的思想方法和应用案例。

2. 工厂模式解析

2.1 工厂模式简介

工厂方法模式(Factory Method Pattern)的主要参与者包括 Creator(抽象工厂)、ConcreteCreator(具体工厂) 、Product(抽象产品)、ConcreteProduct(具体产品),它们的描述如下表所示:

角色 定义或描述
Creator(抽象工厂) 定义创建对象的接口,声明了创建产品对象的抽象方法。
ConcreteCreator(具体工厂) 实现抽象工厂接口,负责创建具体的产品对象。
Product(抽象产品) 定义产品的接口或抽象类,声明了产品的公共方法。
ConcreteProduct(具体产品) 实现抽象产品接口或继承抽象产品类,提供具体的功能。

各种角色及其关系可以使用 UML 绘制结构图如下:

Creator
+createProduct()
ConcreteCreatorA
+createProduct()
ConcreteCreatorB
+createProduct()
Product
+operation()
ConcreteProductA
+operation()
ConcreteProductB
+operation()
Client
图 2: 工厂模式结构图

其中:

  • Creator 类表示抽象工厂(Abstract Factory)。抽象工厂往往是一个接口或抽象类,其中声明了创建产品对象的抽象方法。
  • ConcreteProductA 、ConcreteProductB 类都表示具体工厂(Concrete Factory)。具体工厂实现了抽象工厂接口,负责创建具体的产品对象。
  • Product 类表示抽象产品(Abstract Product)。抽象产品定义了产品的接口或抽象类,其中声明了产品的公共方法。
  • ConcreteProductA、ConcreteProductB 都表示具体产品(Concrete Product)。具体产品实现了抽象产品接口或继承抽象产品类,提供了具体的功能。

这种结构使得客户端可以通过抽象工厂来创建产品对象,而无需关心具体的创建细节和具体的产品类。这样实现了对象的创建和使用的分离,提高了系统的灵活性和可维护性。

2.2 工厂模式的优缺点

介绍完 工厂模式的原理后,我们接着来谈一谈工厂模式的优缺点。工厂方法模式具有以下优点:

  1. 符合开闭原则(Open-Closed Principle):工厂方法模式通过使用抽象工厂和具体工厂类的组合,使得系统的扩展性更好。当需要新增一种角色类型时,只需添加相应的具体工厂类和具体角色类,而无需修改现有的代码。这样就实现了对修改关闭,对扩展开放。

  2. 封装了对象的创建过程:工厂方法模式将对象的创建过程封装在具体工厂类中,客户端无需关心具体的创建细节,只需要通过抽象工厂接口来创建产品对象。这样可以隐藏对象创建的细节,提高了系统的封装性和安全性。

  3. 提供了一种灵活的机制:工厂方法模式允许在运行时动态决定创建哪种类型的对象。客户端可以根据需要选择合适的具体工厂类来创建产品对象,从而实现了对象的动态配置和管理。

  4. 简化了客户端代码:客户端只需要和抽象工厂接口进行交互,而无需直接依赖于具体的产品类。这样简化了客户端的代码,减少了与具体产品类的耦合,提高了代码的可维护性和可测试性。

工厂方法模式也存在一些缺点:

  1. 增加了类的个数:工厂方法模式引入了抽象工厂和具体工厂类,以及相应的抽象产品和具体产品类,因此增加了类的个数,导致系统的结构更加复杂。

  2. 增加了系统的抽象性和理解难度:工厂方法模式引入了抽象工厂和抽象产品的概念,对于新手开发人员来说,理解和掌握这些抽象概念可能需要一定的时间和经验。

  3. 每增加一个具体产品类,就需要增加一个对应的具体工厂类:当系统中新增一种产品类型时,除了需要添加具体产品类外,还需要添加相应的具体工厂类。这增加了系统的扩展性的同时也增加了系统的复杂性。

总之,工厂方法模式适用于需要在运行时动态决定对象创建,并且希望通过扩展系统以支持新的产品类型,同时隐藏对象创建细节的场景。它提供了一种灵活的机制,使得系统可以根据需要创建具体的产品对象,并且能够方便地进行扩展和维护。然而,开发人员在使用工厂方法模式时需要权衡好增加的抽象性和理解难度,以及类的个数增加所带来的复杂性。

3. 应用实例

现在我们应用工厂模式来处理引例中的游戏角色构建问题。我们可以将人物角色( Character )看作由角色工厂 (CharacterFactory) 完成创建。也就是 Character 相当于抽象产品,而 CharacterFactory 为对应的抽象工厂。

这样,当我们从 产品 一侧看时,Character 类泛化为 WarriorMageRogueHunterPaladin 这五个具体产品(ConcreteProduct):

Character
+name: String
+health: int
+attack()
Warrior
+name: String
+health: int
+attack()
Mage
+name: String
+health: int
+attack()
Rogue
+name: String
+health: int
+attack()
Hunter
+name: String
+health: int
+attack()
Paladin
+name: String
+health: int
+attack()
图 3: 抽象与具体产品(人物角色)

对应的代码实现如下:

// 抽象产品
abstract class Character {
    
    
  late String name;
  late int health;

  void attack();
}

// 具体产品
class Warrior implements Character {
    
    
  String name;
  int health;

  Warrior({
    
    required this.name, required this.health});

  
  void attack() {
    
    
    print('Warrior attacking!');
  }
}

// 具体产品
class Mage implements Character {
    
    
  String name;
  int health;

  Mage({
    
    required this.name, required this.health});

  
  void attack() {
    
    
    print('Mage attacking!');
  }
}

// 具体产品
class Rogue implements Character {
    
    
  String name;
  int health;

  Rogue({
    
    required this.name, required this.health});

  
  void attack() {
    
    
    print('Rogue attacking!');
  }
}

// 具体产品
class Hunter implements Character {
    
    
  String name;
  int health;

  Hunter({
    
    required this.name, required this.health});

  
  void attack() {
    
    
    print('Hunter attacking!');
  }
}

// 具体产品
class Paladin implements Character {
    
    
  String name;
  int health;

  Paladin({
    
    required this.name, required this.health});

  
  void attack() {
    
    
    print('Paladin attacking!');
  }
}

当我们从 工厂 一侧看时,CharacterFactory 类泛化为 WarriorFactoryMageFactoryRogueFactoryHunterFactoryPaladinFactory 这五个具体工厂(ConcreteFactory):

CharacterFactory
+createCharacter(type: String)
WarriorFactory
+createCharacter()
MageFactory
+createCharacter()
RogueFactory
+createCharacter()
HunterFactory
+createCharacter()
PaladinFactory
+createCharacter()
图 4: 抽象与具体工厂(人物角色工厂)

对应的代码实现如下:

// 抽象工厂
abstract class CharacterFactory {
    
    
  Character createCharacter();
}

// 具体工厂
class WarriorFactory implements CharacterFactory {
    
    
  
  Character createCharacter() {
    
    
    return Warrior(name: 'Warrior', health: 100);
  }
}

// 具体工厂
class MageFactory implements CharacterFactory {
    
    
  
  Character createCharacter() {
    
    
    return Mage(name: 'Mage', health: 80);
  }
}

// 具体工厂
class RogueFactory implements CharacterFactory {
    
    
  
  Character createCharacter() {
    
    
    return Rogue(name: 'Rogue', health: 90);
  }
}

// 具体工厂
class HunterFactory implements CharacterFactory {
    
    
  
  Character createCharacter() {
    
    
    return Hunter(name: 'Hunter', health: 90);
  }
}

// 具体工厂
class PaladinFactory implements CharacterFactory {
    
    
  
  Character createCharacter() {
    
    
    return Paladin(name: 'Paladin', health: 120);
  }
}

现在我们创建某个具体产品时(如创建战士)不再直接使用 new 关键字,而是使用相应的具体工厂进行创建。

现在你可以使用工厂来创建具体角色对象,例如:

CharacterFactory warriorFactory = WarriorFactory();
Character warrior = warriorFactory.createCharacter();
warrior.attack();

CharacterFactory mageFactory = MageFactory();
Character mage = mageFactory.createCharacter();
mage.attack();

CharacterFactory rogueFactory = RogueFactory();
Character rogue = rogueFactory.createCharacter();
rogue.attack();

CharacterFactory hunterFactory = HunterFactory();
Character hunter = hunterFactory.createCharacter();
hunter.attack();

CharacterFactory paladinFactory = PaladinFactory();
Character paladin = paladinFactory.createCharacter();
paladin.attack();

其输出结果为:

Warrior attacking!
Mage attacking!
Rogue attacking!
Hunter attacking!
Paladin attacking!

猜你喜欢

转载自blog.csdn.net/qq_28550263/article/details/131729619