文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

三言两语说透设计模式的艺术-简单工厂模式

2024-11-30 10:14

关注

二、游戏工厂的设计

ONEGAME游戏公司计划开发一条游戏生产线,该生产线可以向玩家提供不同类型的游戏,例如:RGP游戏、MMORGP游戏、MOBA游戏以及FPS游戏等。为了提供这些游戏,游戏公司需要创建一个游戏工厂,来创建这些游戏的实例。

ONEGAME游戏公司提出了初始设计方案,就是将所有类型的游戏的实现代码封装到一个Game类中,然后通过Game工厂来创建实例。实现代码如下:

class Game{
  private type: string;//游戏类别
  constructor(type: string, data: any) {
    this.type = type;
    if(type.toLocaleLowerCase() === 'fps'){
      // 初始化FPS游戏
    }else if(type.toLocaleLowerCase() === 'rpg'){
      // 初始化RPG游戏
    }else if(type.toLocaleLowerCase() === 'moba'){
      // 初始化MOBA游戏
    }
  }

  play(){
    if(this.type.toLocaleLowerCase() === 'fps'){
      // 玩FPS游戏
    }else if(this.type.toLocaleLowerCase() === 'rpg'){
      // 玩RPG游戏
    }else if(this.type.toLocaleLowerCase() === 'moba'){
      // 玩MOBA游戏
    }
  }
}

上面的代码实现了游戏的创建和玩游戏的功能,但是这样的设计存在以下问题:

为了解决上面的问题,我们可以对Game类进行重构,将其拆分成多个游戏类,每个游戏类只负责自己的初始化和玩游戏的功能,这样就可以避免代码臃肿和违反单一职责原则的问题。但是这样做还是无法解决对象创建和使用无法分离的问题,我们可以通过简单工厂模式来解决这个问题。

三、简单工厂模式

简单工厂的设计思想就是,将创建不同对象的相关的代码封装到不同的类中,即具体产品类,这样就可以避免代码的臃肿和违反单一职责原则的问题。将它们的公共代码抽象到和封装到一个抽象产品类中,每个具体类都是抽象产品类的子类。然后通过一个工厂类来创建这些具体产品类的实例,通过传入的参数不同创建对应的具体产品对象。

什么是简单工厂模式

简单工厂模式:定义一个工厂类,通过传入参数来创建不同的具体产品类的实例,被创建的实例都具有共同的父类。

简单工厂模式结构包括三个角色:

使用简单工厂模式优化上面的代码,以实现一个游戏工厂为为例,实现可以生产不同类型的游戏为目的。首先定义一个抽象产品类Game,然后定义具体产品类FPSGame、RPGGame、MOBAGame,最后定义一个工厂类GameFactory,通过传入不同的参数来创建不同的游戏实例。

// 游戏接口:抽象产品类
interface Game {
  play(): void;
}

// 各种游戏的具体实现类:具体产品类
// FPS游戏
class FPSGame implements Game{
  play() {
    console.log('FPS游戏');
  }
}

// RPG游戏
class RPGGame implements Game {
  play() {
    console.log('RPG游戏');
  }
}

// MOBA游戏
class MOBAGame implements Game {
  play() {
    console.log('MOBA游戏');
  }
}

// 游戏工厂:创建具体产品类的实例的工厂类
class GameFactory {
  static createGame(type: string): Game {
    this.type = type;
    switch (this.type) {
      case 'RPG':
        return new RPGGame();
      case 'MOBA':
        return new MOBAGame();
      case 'FPS':
        return new FPSGame();
      default:
        throw new Error('Unknown game type');
    }
  }
}

用户实际使用创建对应的游戏:

// 获取RGP游戏
const rgpGame = GameFactory.createGame('RPG');
rgpGame.play();

// 获取MOBA游戏
const mobaGame = GameFactory.createGame('MOBA');
mobaGame.play();

在实际使用中,客户端代码只需要传入类型参数,就可以获取得到对应的游戏对象,而不需要关系对象的具体实现。这就符合简单工厂模式的设计思想。

简单工厂模式的优点

四、简单工厂模式的优化

使用泛型优化工厂类

在上面的实现中,工厂类的创建方法返回的是Game接口类型,缺点是客户端得到的对象类型信息不全,对此可以使用泛型来改进:

// 游戏接口:抽象产品类
interface Game {
  play(): void;
}

class FPSGame implements Game {
  //...
}

class RPGGame implements Game {
  //...
}

class MOBAGame implements Game {
  //...
}

class GameFactory{
  static createGame(type: string): T{
    //...
  }
}

这样在客户端代码得到的对象类型信息更加准确。

const rgpGame = GameFactory.createGame('RPG');
//  rgpGame的类型是RPGGame,而不是Game

使用泛型优化工厂类的优化

上面的代码中,所有的产品类都需要实现 Game 接口,这样会存在代码重复的问题。我们可以引入一个泛型接口 IGame来改进:

interface IGame {
  play(): void;
  info(): T; 
}

class RPGGame implements IGame {
  play() {
    // ...
  }
  
  info() {
    return 'RPG'; 
  }
}

class MOBAGame implements IGame {
  play() {
    // ...
  }
  
  info() {
    return 'MOBA';
  }
}

class FPSGame implements IGame {
  // ...
}

这样每个产品类就可以定制自己的 info 方法返回值类型了。

使用抽象类改进产品类

上面的代码还存在问题:所有产品类都需要实现 play 方法,这会导致重复代码。我们可以使用抽象类来解决这个问题:

abstract class GameBase {
  play() {
    // 默认游戏逻辑
  } 
}

class RPGGame extends GameBase implements IGame {
  info() {
    return 'RPG';
  }
}

class MOBAGame extends GameBase implements IGame {
  // ...
}

class FPSGame extends GameBase implements IGame {
  // ...
}

这样产品类就不需要重复实现 play 方法了,只需要继承 GameBase 并实现 info 方法即可。

使用配置文件创建工厂类

上面的代码中,工厂类的创建方法需要传入一个类型参数,这样会导致客户端代码需要知道具体的类型参数,这样就会破坏简单工厂模式的封装性。我们可以使用配置文件来解决这个问题:

class GameConfig {
  static gameTypes = {
    'RPG': RPG,
    'MOBA': MOBA,
    'FPS': FPS
  }
}

工厂类读取配置创建对象:

class GameFactory {
  static createGame(type: string) {
    const Constructor = GameConfig.gameTypes[type];
    if (!Constructor) {
      throw new Error('Unknown type');  
    }
    return new Constructor();
  }
}

这样当需要新增游戏类型时,只需要在配置类中添加新的类型和类即可,工厂类的代码无需修改。

利用依赖注入实现解耦

我们还可以通过依赖注入进一步解耦:

@injectable()
class GameFactory {

  constructor(
    @inject(GameConfig.gameTypes.RPG) private rpgGame: Game,
    @inject(GameConfig.gameTypes.MOBA) private mobaGame: Game,
    @inject(GameConfig.gameTypes.FPS) private fpsGame: Game
  ) {}

  createGame(type: string) {
    switch(type) {
      // ...
    }
  }
}

这样工厂类不再负责创建对象,而是通过注入的方式获取对象实例,大大提升了灵活性。

五、完整代码示例

下面是使用 TypeScript 深入解析简单工厂模式的示例,通过工厂类和产品类的抽象与解耦,可以实现创建对象逻辑的集中和优化,提高代码的灵活性和扩展性。TypeScript 通过接口、泛型和抽象类等特性增强了简单工厂模式的实现。掌握设计模式对编写优雅可扩展的 TypeScript 代码很有帮助。

// 游戏接口
interface Game {
  play(): void;
}

// 泛型游戏接口 
interface IGame {
  play(): void;
  info(): T;
}

// 抽象游戏类
abstract class GameBase {
  play() {
    console.log('Playing game...');
  }
}

// RPG游戏类
class RPG extends GameBase implements IGame {
  info() {
    return 'RPG'; 
  }
}

// MMORPG游戏类  
class MMORPG extends GameBase implements IGame {
  info() {
    return 'MMORPG';
  }
}

// FPS游戏类
class FPS extends GameBase implements IGame {
  info() {
    return 'FPS'; 
  }
}

// 配置类
class GameConfig {
  static gameTypes = {
    'RPG': RPG,
    'MMORPG': MMORPG,
    'FPS': FPS
  }
}

// 工厂类
class GameFactory {
  static createGame(type: string) {
    const Constructor = GameConfig.gameTypes[type];
    if (!Constructor) {
      throw new Error('Unknown type');
    }
    return new Constructor();
  }
}

// 客户端
const rpgGame = GameFactory.createGame('RPG');
rpgGame.play();
console.log(rpgGame.info());

const fpsGame = GameFactory.createGame('FPS');
fpsGame.play();
console.log(fpsGame.info());

六、简单工厂模式和单例模式的区别

1. 用途不同

简单工厂模式是一种创建对象的设计模式,它通过工厂类来创建产品对象,主要目的是将对象创建的过程封装起来,便于管理和维护。

而单例模式是一种确保某个类只有一个实例的设计模式,它的目的是在整个软件系统中,对某个类只创建一个对象实例,避免浪费资源。

2. 实现方式不同

简单工厂模式是通过工厂类的静态方法创建对象实例,可以创建多个实例。

单例模式是在类中定义一个静态变量保存单例实例,并通过一个静态方法来获取这个实例,确保只创建一个实例。

3. 使用场景不同

简单工厂模式用于创建同一类产品的不同对象实例,客户端无需知道具体产品类的类名。

单例模式用于创建对唯一实例有需求的对象,如线程池、缓存、日志对象等。

小结一下,简单工厂模式关注创建不同实例,单例模式关注如何只创建一个实例。二者解决的问题和应用场景不同,但可以结合使用,工厂类可以返回单例对象。

七、总结

通过上面的示例,我们使用 TypeScript 从多个方面对简单工厂模式进行了深入解析,包括:

简单工厂模式的优点:

简单工厂模式的缺点:

简单工厂模式通过工厂类和产品类的解耦,可以实现创建对象逻辑的集中化和优化,是非常常用和灵活的一种设计模式。TypeScript 通过接口、泛型和抽象类等特性,可以更优雅地实现简单工厂模式,提高代码的复用性和扩展性。

来源:宇宙一码平川内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯