Node.js 设计模式笔记 —— Strategy 模式

Strategy 模式的主体是一个 context 对象,再把逻辑中有变化的部分抽取到独立的可相互替换的 strategy 对象中,从而使 context 支持不同的策略。即 context 实现通用的逻辑,strategy 实现可替换的部分。context 与 不同的 strategy 相组合即产生了多种不同的实现。

Strategy

就像雨天穿胶鞋,打篮球穿运动鞋,短跑比赛穿跑鞋。这些不同的鞋子对应的就是一系列策略,它们是同一类对象的不同变种,彼此之间可以相互替换。
面对不同的使用场景,选择对应的策略即可,这带来了更多的灵活性。首先鞋子和人不能是绑定的,这样的话,换鞋子就需要同时换掉整个人了;其次也没有任何一双鞋可以同时满足所有的使用场景。让鞋子作为可替换的插件无疑是最直观和方便的。
总的来说,策略代表了一个对象中可替换的部分。不同的策略应对同一个问题的不同变种。静态与动态分离。

比如需要实现一个 Order 对象,代表在线商城中的订单。该对象有一个 pay() 方法,负责支付行为,将用户的钱转移到商户手中。为了能够支持多种不同的支付方式,可以有以下两种选项:

  • pay() 方法中使用 if...else,根据不同的支付方式,完成对应的支付动作
  • 将支付的具体逻辑移交给独立的 strategy 对象,用户选择支付方式后,将对应的 strategy 注入到 Order

对于第一种方案,当 Order 对象需要支持更多的支付方式时,就必须要修改 Order 本身的代码。这会使代码变得非常复杂,难以维护。
当使用第二种 Strategy 模式时,理论上可以支持无限多的支付方式。Order 只负责维护用户、商品条目、价格等信息,具体的支付逻辑则由另一个 Strategy 对象来实现。Order 本身不会由于支付方式的增加而发生任何变更。

实例:支持 JSON、INI 等多种格式的 config 对象

mkdir strategy && cd strategy
npm install ini
npm install object-path

package.json

{
  "type": "module",
  "dependencies": {
    "ini": "^3.0.0",
    "object-path": "^0.11.8"
  }
}

config.js

import {promises as fs} from 'fs'
import objectPath from 'object-path'

export class Config {
  constructor(formatStrategy) {
    this.data = {}
    this.formatStrategy = formatStrategy
  }

  get(configPath) {
    return objectPath.get(this.data, configPath)
  }

  set(configPath, value) {
    return objectPath.set(this.data, configPath, value)
  }

  async load(filePath) {
    console.log(`Deserializing from ${filePath}`)
    this.data = this.formatStrategy.deserialize(
      await fs.readFile(filePath, 'utf-8')
    )
  }

  async save(filePath) {
    console.log(`Serializing to ${filePath}`)
    await fs.writeFile(filePath,
      this.formatStrategy.serialize(this.data))
  }
}

其中构造函数 constructor 接收一个具体的策略对象 formStrategy 作为参数,之后的 loadsave 方法又使用这个 formStrategy 去执行与格式相关的序列化和反序列化操作。不同的 formStrategy 有着不同的实现,从而 Config 类可以凭借 construcotr 接收的不同参数,与不同的策略整合,灵活地应对不同的需求场景。

strategy.js

import ini from 'ini'

export const iniStrategy = {
  deserialize: data => ini.parse(data),
  serialize: data => ini.stringify(data)
}

export const jsonStrategy = {
  deserialize: data => JSON.parse(data),
  serialize: data => JSON.stringify(data, null, ' ')
}

此处的代码实现了两种不同的策略:iniStrategyjsonStrategy,分别针对不同的文件格式。它们有着一致的接口,符合策略之间可以相互替换的原则,从而都可以被前面 Config 类的 loadsave 方法调用。

index.js

import {Config} from './config.js'
import {jsonStrategy, iniStrategy} from './strategy.js'

async function main() {
  const iniConfig = new Config(iniStrategy)
  await iniConfig.load('samples/conf.ini')
  iniConfig.set('book.nodejs', 'design patterns')
  await iniConfig.save('samples/conf_mod.ini')

  const jsonConfig = new Config(jsonStrategy)
  await jsonConfig.load('samples/conf.json')
  jsonConfig.set('book.nodejs', 'design patterns')
  await jsonConfig.save('samples/conf_mod.json')
}

main()

参考资料

Node.js Design Patterns: Design and implement production-grade Node.js applications using proven patterns and techniques, 3rd Edition

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1 场景问题# 1.1 报价管理## 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的...
    七寸知架构阅读 5,266评论 9 62
  • 1 场景问题 1.1 报价管理 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的客户要...
    4e70992f13e7阅读 3,233评论 2 16
  • 1.初识策略模式 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户...
    王侦阅读 1,550评论 0 3
  • 1. 什么是策略模式? 策略模式 定义了算法族,分别封装起来, 让它们之间可以互相替换,此模式让算法的变化独立于使...
    从你说谎阅读 350评论 0 0
  • 策略模式 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模...
    javacoo阅读 380评论 0 1

友情链接更多精彩内容