当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-08-05 11:44:26 2793观看
导读一、写在前面工厂模式是最常见的一种创建型设计模式,通常说的工厂模式指的是工厂方法模式,是使用频率最高的工厂模式。简单工厂模式又称为静态工厂方法模式,不属于GoF 23种设计模式,它属于类创建型模式,是其它工厂模式的入

一、写在前面

工厂模式是最常见的一种创建型设计模式,通常说的工厂模式指的是工厂方法模式,是使用频率最高的工厂模式。简单工厂模式又称为静态工厂方法模式,不属于GoF 23种设计模式,它属于类创建型模式,是其它工厂模式的入门。0Jw28资讯网——每日最新资讯28at.com

二、游戏工厂的设计

ONEGAME游戏公司计划开发一条游戏生产线,该生产线可以向玩家提供不同类型的游戏,例如:RGP游戏、MMORGP游戏、MOBA游戏以及FPS游戏等。为了提供这些游戏,游戏公司需要创建一个游戏工厂,来创建这些游戏的实例。0Jw28资讯网——每日最新资讯28at.com

ONEGAME游戏公司提出了初始设计方案,就是将所有类型的游戏的实现代码封装到一个Game类中,然后通过Game工厂来创建实例。实现代码如下:0Jw28资讯网——每日最新资讯28at.com

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游戏    }  }}

上面的代码实现了游戏的创建和玩游戏的功能,但是这样的设计存在以下问题:0Jw28资讯网——每日最新资讯28at.com

  • Game类的代码会越来越臃肿,而且违反了单一职责原则,不利于代码的维护和扩展。
  • 在需要对Game类进行扩展新游戏的时候,需要对源码进行修改,这就违反了开闭原则。
  • 用户只能通过new关键字来直接创建实例,而不是通过Game工厂来创建实例,耦合性高,对象创建和使用无法分离。

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

三、简单工厂模式

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

什么是简单工厂模式

简单工厂模式:定义一个工厂类,通过传入参数来创建不同的具体产品类的实例,被创建的实例都具有共同的父类。0Jw28资讯网——每日最新资讯28at.com

简单工厂模式结构包括三个角色:0Jw28资讯网——每日最新资讯28at.com

  • 工厂类:创建具体产品类的实例的工厂类,负责实现创建具体产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
  • 抽象产品类:创建具体产品类的实例的抽象产品类,它是需要工厂类所创建的所有具体产品类的公共父类,封装了各种产品对象的公有方法。
  • 具体产品类:具体产品类的实例,是简单工厂模式的创建目标,它继承抽象产品类的公共父类,所有被创建的对象都是这个产品对应的具体产品类的实例。

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

// 游戏接口:抽象产品类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');    }  }}

用户实际使用创建对应的游戏:0Jw28资讯网——每日最新资讯28at.com

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

在实际使用中,客户端代码只需要传入类型参数,就可以获取得到对应的游戏对象,而不需要关系对象的具体实现。这就符合简单工厂模式的设计思想。0Jw28资讯网——每日最新资讯28at.com

简单工厂模式的优点

  • 封装实例化过程:简单工厂模式在一个工厂类中封装了实例化对象的过程,创建对象的细节被隐藏在工厂类中,外界无需关心对象是如何被创建出来的。
  • 定义统一接口:工厂类所创建的对象都实现了一个共同的接口,对外提供一致的使用方式。
  • 通过参数创建不同实例:客户端只需要传入不同的参数给工厂类,就可以获取不同的对象实例,而不需要知道具体类名。
  • 实现解耦:简单工厂模式实现了客户端与产品类的解耦,客户端不需要知道所创建对象的具体实现,只需要知道参数即可。
  • 优化开闭原则:当需要新增一个产品类时,只需要实现统一接口,然后在工厂类中添加对应类型即可,无需修改客户端代码。
  • 高内聚,低耦合:每个产品类只关心自己的功能实现,不关心实例化过程;客户端不依赖具体类。使代码内聚性高、耦合度低。

四、简单工厂模式的优化

使用泛型优化工厂类

在上面的实现中,工厂类的创建方法返回的是Game接口类型,缺点是客户端得到的对象类型信息不全,对此可以使用泛型来改进:0Jw28资讯网——每日最新资讯28at.com

// 游戏接口:抽象产品类interface Game {  play(): void;}class FPSGame implements Game {  //...}class RPGGame implements Game {  //...}class MOBAGame implements Game {  //...}class GameFactory{  static createGame<T extends Game>(type: string): T{    //...  }}

这样在客户端代码得到的对象类型信息更加准确。0Jw28资讯网——每日最新资讯28at.com

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

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

上面的代码中,所有的产品类都需要实现 Game 接口,这样会存在代码重复的问题。我们可以引入一个泛型接口 IGame来改进:0Jw28资讯网——每日最新资讯28at.com

interface IGame<T> {  play(): void;  info(): T; }class RPGGame implements IGame<string> {  play() {    // ...  }    info() {    return 'RPG';   }}class MOBAGame implements IGame<string> {  play() {    // ...  }    info() {    return 'MOBA';  }}class FPSGame implements IGame<string> {  // ...}

这样每个产品类就可以定制自己的 info 方法返回值类型了。0Jw28资讯网——每日最新资讯28at.com

使用抽象类改进产品类

上面的代码还存在问题:所有产品类都需要实现 play 方法,这会导致重复代码。我们可以使用抽象类来解决这个问题:0Jw28资讯网——每日最新资讯28at.com

abstract class GameBase {  play() {    // 默认游戏逻辑  } }class RPGGame extends GameBase implements IGame<string> {  info() {    return 'RPG';  }}class MOBAGame extends GameBase implements IGame<string> {  // ...}class FPSGame extends GameBase implements IGame<string> {  // ...}

这样产品类就不需要重复实现 play 方法了,只需要继承 GameBase 并实现 info 方法即可。0Jw28资讯网——每日最新资讯28at.com

使用配置文件创建工厂类

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

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

工厂类读取配置创建对象:0Jw28资讯网——每日最新资讯28at.com

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

这样当需要新增游戏类型时,只需要在配置类中添加新的类型和类即可,工厂类的代码无需修改。0Jw28资讯网——每日最新资讯28at.com

利用依赖注入实现解耦

我们还可以通过依赖注入进一步解耦:0Jw28资讯网——每日最新资讯28at.com

@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) {      // ...    }  }}

这样工厂类不再负责创建对象,而是通过注入的方式获取对象实例,大大提升了灵活性。0Jw28资讯网——每日最新资讯28at.com

五、完整代码示例

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

// 游戏接口interface Game {  play(): void;}// 泛型游戏接口 interface IGame<T> {  play(): void;  info(): T;}// 抽象游戏类abstract class GameBase {  play() {    console.log('Playing game...');  }}// RPG游戏类class RPG extends GameBase implements IGame<string> {  info() {    return 'RPG';   }}// MMORPG游戏类  class MMORPG extends GameBase implements IGame<string> {  info() {    return 'MMORPG';  }}// FPS游戏类class FPS extends GameBase implements IGame<string> {  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>('RPG');rpgGame.play();console.log(rpgGame.info());const fpsGame = GameFactory.createGame<FPS>('FPS');fpsGame.play();console.log(fpsGame.info());

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

1. 用途不同

简单工厂模式是一种创建对象的设计模式,它通过工厂类来创建产品对象,主要目的是将对象创建的过程封装起来,便于管理和维护。0Jw28资讯网——每日最新资讯28at.com

而单例模式是一种确保某个类只有一个实例的设计模式,它的目的是在整个软件系统中,对某个类只创建一个对象实例,避免浪费资源。0Jw28资讯网——每日最新资讯28at.com

2. 实现方式不同

简单工厂模式是通过工厂类的静态方法创建对象实例,可以创建多个实例。0Jw28资讯网——每日最新资讯28at.com

单例模式是在类中定义一个静态变量保存单例实例,并通过一个静态方法来获取这个实例,确保只创建一个实例。0Jw28资讯网——每日最新资讯28at.com

3. 使用场景不同

简单工厂模式用于创建同一类产品的不同对象实例,客户端无需知道具体产品类的类名。0Jw28资讯网——每日最新资讯28at.com

单例模式用于创建对唯一实例有需求的对象,如线程池、缓存、日志对象等。0Jw28资讯网——每日最新资讯28at.com

小结一下,简单工厂模式关注创建不同实例,单例模式关注如何只创建一个实例。二者解决的问题和应用场景不同,但可以结合使用,工厂类可以返回单例对象。0Jw28资讯网——每日最新资讯28at.com

七、总结

通过上面的示例,我们使用 TypeScript 从多个方面对简单工厂模式进行了深入解析,包括:0Jw28资讯网——每日最新资讯28at.com

  • 使用泛型优化工厂方法的返回类型
  • 使用泛型接口减少产品类代码重复
  • 使用抽象类提取产品类公共代码
  • 使用配置文件动态创建产品类实例

简单工厂模式的优点:0Jw28资讯网——每日最新资讯28at.com

  • 将对象创建的过程封装在工厂类中,实现了解耦
  • 客户端无须知道所创建具体产品类的类名
  • 可以方便地扩展新的具体产品类

简单工厂模式的缺点:0Jw28资讯网——每日最新资讯28at.com

  • 工厂类职责较重,所有产品创建逻辑都集中在工厂类
  • 如果产品类型较多,工厂类会变得很复杂
  • 扩展新的产品困难,需要修改工厂类代码

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

本文链接:http://www.28at.com/showinfo-26-73-0.html三言两语说透设计模式的艺术-简单工厂模式

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Raft算法:保障分布式系统共识的稳健之道

下一篇: 28个SpringBoot项目中常用注解,日常开发、求职面试不再懵圈

标签:
  • 热门焦点
  • 5月iOS设备性能榜:M1 M2依旧是榜单前五

    5月iOS设备性能榜:M1 M2依旧是榜单前五

    和上个月一样,没有新品发布的iOS设备性能榜的上榜设备并没有什么更替,仅仅只有跑分变化而产生的排名变动,刚刚开始的苹果WWDC2023,推出的产品也依旧是新款Mac Pro、新款Mac Stu
  • SpringBoot中使用Cache提升接口性能详解

    SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • 腾讯盖楼,字节拆墙

    腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • 消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    来源:征探财经作者:陈香羽随着流量红利的退潮,电商的存量博弈越来越明显。曾经主攻中高端与品质的淘宝天猫、京东重拾&ldquo;低价&rdquo;口号。而过去与他们错位竞争的拼多多,靠
  • 网红炒股不为了赚钱,那就是耍流氓!

    网红炒股不为了赚钱,那就是耍流氓!

    来源:首席商业评论6月26日高调宣布入市,网络名嘴大v胡锡进居然进军了股市。在一次财经媒体峰会上,几个财经圈媒体大佬就&ldquo;胡锡进炒股是否知道认真报道&rdquo;展开讨论。有
  • 小米公益基金会捐赠2500万元驰援北京、河北暴雨救灾

    小米公益基金会捐赠2500万元驰援北京、河北暴雨救灾

    8月2日消息,今日小米科技创始人雷军在其微博上发布消息称,小米公益基金会宣布捐赠2500万元驰援北京、河北暴雨救灾。携手抗灾,京冀安康!以下为公告原文
  • 2纳米决战2025

    2纳米决战2025

    集微网报道 从三强争霸到四雄逐鹿,2nm的厮杀声已然隐约传来。无论是老牌劲旅台积电、三星,还是誓言重回先进制程领先地位的英特尔,甚至初成立不久的新
  • 英特尔Xe-HP项目终止,将专注Xe-HPC/HPG系列显卡

    英特尔Xe-HP项目终止,将专注Xe-HPC/HPG系列显卡

    据10 月 31 日消息报道,英特尔高级副总裁兼加速计算系统和图形事业部总经理 表示,Xe-HP“ Arctic Sound” 系列服务器 GPU 已经应用于 oneAPI devcloud 云服
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top