首页 > 其他分享 >js 函数如何实现策略模式与状态模式

js 函数如何实现策略模式与状态模式

时间:2023-10-16 17:27:13浏览次数:37  
标签:function 状态 return 策略 模式 js context 函数

前言

有关设计模式的学习资料中,大部分都是以 java 语言实现的,毕竟 java 作为老牌面向对象的语言最能说明设计模式的核心概念,所以 js 的相关设计模式的学习资料也大多使用 class 类实现,本文记录下 js 使用函数实现策略模式和状态模式设计模式的方式,更有助于理解策略模式和状态模式如何在实际工作中运用。

策略模式

定义一系列的算法,把它们一个一个封装起来,并且使它们可以相互替换。

目的:将算法的使用和算法实现分离开来

优点:

  • 利用组合、委托、多态等思想,可以解决多重条件选择语句问题
  • 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展
  • 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作
  • 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案

缺点:

  • 代码会增加许多策略类和策略对象
  • 需要全面了解各种 stragety, stragety要向客户暴露它的所有实现,违反最少知识原则

状态模式

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

优点:

  • 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。通过增加新的状态类,很容易增加新的状态和转换
  • 避免 Context 无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了 Context 中原本过多的条件分支
  • 用对象代替字符串来记录当前状态,使得状态的切换更加一目了然
  • Context 中的请求动作和状态类中封装的行为可以非常容易地独立变化而互不影响

缺点:

  • 会在系统中定义许多状态类,而且系统中会因此而增加不少对象

         性能优化点

    • 1、仅当 state 对象被需要时才创建并随后销毁,用于节省内存,但不常变动的
    • 2、一开始就创建好所有的状态对象,并且始终不销毁它们,用于状态经常变动的
  • 由于逻辑分散在状态类中,虽然避开了不受欢迎的条件分支语句,但也造成了逻辑分散的问题,我们无法在一个地方就看出整个状态机的逻辑

相同点

  • 都有一个上下文、一些策略或状态类
  • 上下文把请求委托给这些类来执行

区别

状态模式

  • 【不同事情 】状态模式各状态之间的切换,做不同的事情;
  • 【不能互相替换 】状态模式各个状态的同一方法做的是不同的事,不能互相替换,状态和状态行为是早已被封封装好的,状态之间的切换也早被规定完成,改变模式这个行为发生在状态内部,使用者不需要了解改变的细节;
  • 【封装状态】状态模式封装了对象的状态;
  • 【状态不可重用】因为状态是跟对象密切相关的,它不能被重用;
  • 【持有context 】在状态模式中,每个状态通过持有Context的引用,来实现状态转移;。

策略模式

  • 【同样的事情】策略模式更侧重于根据具体情况选择策略,做同样的事情;
  • 【可替换】策略模式各个策略之间是可替换的,平等又平行,互相之间没有任何联系,需熟知各个策略、各类的作用,以便随时切换算法;
  • 【封装算法和策略】策略模式封装算法或策略;
  • 【策略可重用】策略模式通过从Context中分离出策略或算法,我们可以重用它们;
  • 【不持有context】但是每个策略都不持有Context的引用,它们只是被Context使用。context持有对某个策略对象的引用。
状态模式关注于对象的状态转换,而策略模式关注于将算法或行为封装到策略对象中,以便在运行时动态替换。

联系

状态模式和策略模式都是为具有多种可能情形设计的模式,把不同的处理情形抽象为一个相同的接口,符合对扩展开放,对修改封闭的原则。(优化多个条件判断语句的业务逻辑)

实践

策略模式

var S = function( salary ){ 
  return salary * 4; 
}; 
var A = function( salary ){ 
  return salary * 3; 
}; 
var B = function( salary ){ 
  return salary * 2; 
}; 
// 这里的 context 是 calculateBonus
var calculateBonus = function( func, salary ){ 
  return func( salary ); 
}; 
calculateBonus( S, 10000 ); // 输出:40000
// 定义各个策略对象,每个策略对象都不持有 context 的引用,仅仅被 context 使用
const add = {
  calculate: function (a, b) {
    return a + b;
  },
};

const subtract = {
  calculate: function (a, b) {
    return a - b;
  },
};

const multiply = {
  calculate: function (a, b) {
    return a * b;
  },
};

// 策略管理器,这里 Calculator 代表了 context,context 拥有执行不同算法的能力,传参为要执行的策略,context 持有对某个策略对象的引用
function Calculator(strategy) {
  this.strategy = strategy;

  this.setStrategy = function (newStrategy) {
    this.strategy = newStrategy;
  };

  this.calculate = function (a, b) {
    return this.strategy.calculate(a, b);
  };
}

// 使用
// 传入或设置不同的策略,执行结果函数得到结果
const calculator = new Calculator(add);
console.log(calculator.calculate(2, 3)); // 5
calculator.setStrategy(subtract);
console.log(calculator.calculate(2, 3)); // -1
calculator.setStrategy(multiply);
console.log(calculator.calculate(2, 3)); // 6

状态模式

/ 状态对象
// 每个状态对象持有对传入状态的引用,以便流转至下一个状态
const redLight = {
  name: '红灯',
  nextState: function () {
    return greenLight;
  },
};

const yellowLight = {
  name: '黄灯',
  nextState: function () {
    return redLight;
  },
};

const greenLight = {
  name: '绿灯',
  nextState: function () {
    return yellowLight;
  },
};

// 状态管理器
function TrafficLightManager(initialState) {
  this.state = initialState;

  this.changeState = function () {
    this.state = this.state.nextState();
  };
}

// 使用
// 传入初始状态
const trafficLight = new TrafficLightManager(redLight);
console.log(trafficLight.state.name); // 红灯
// 通过状态流转方法切换下一个状态
trafficLight.changeState();
console.log(trafficLight.state.name); // 绿灯
trafficLight.changeState();
console.log(trafficLight.state.name); // 黄灯
// 封装执行状态的请求
var delegate = function (client, delegation) {
  return {
    buttonWasPressed: function () { // 将客户的操作委托给 delegation 对象
      return delegation.buttonWasPressed.apply(client, arguments);
    }
  }
};

// 定义状态机
var FSM = {
  off: {
    buttonWasPressed: function () {
      console.log('关灯');
      this.button.innerHTML = '下一次按我是开灯';
      this.currState = this.onState;
    }
  },
  on: {
    buttonWasPressed: function () {
      console.log('开灯');
      this.button.innerHTML = '下一次按我是关灯';
      this.currState = this.offState;
    }
  }
};

// 这里 Light 代表了 context,context 持有各个状态对象的引用,根据状态的改变执行不同的行为
var Light = function () {
  this.offState = delegate(this, FSM.off);
  this.onState = delegate(this, FSM.on);
  this.currState = this.offState; // 设置初始状态为关闭状态
  this.button = null;
};

Light.prototype.init = function () {
  var button = document.createElement('button'),
    self = this;
  button.innerHTML = '已关灯';
  this.button = document.body.appendChild(button);
  this.button.onclick = function () {
    self.currState.buttonWasPressed();
  }
};

var light = new Light()

 

本文大部分总结自书《javascript设计模式与开发实践》,仅供学习使用,如有错误,望大家多多指正。

标签:function,状态,return,策略,模式,js,context,函数
From: https://www.cnblogs.com/beileixinqing/p/17767827.html

相关文章

  • Java设计模式
    七大设计原则开闭原则:是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭依赖倒置原则:是指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象而不依赖于具体。单一职责原则:是指一个Class/Interface/Method只负责一项职责。接口隔离原则:是指用多个专......
  • SaaS模式相较传统CRM系统有何优势?
     SaaS模式的CRM客户管理系统相较于传统的CRM客户管理系统更加方便灵活,更加符合如今的市场环境。它解决了传统CRM系统投入大、维护难的难题,降低了中小企业导入CRM的门槛。下面详细说说SaaS模式相较传统CRM系统有何优势。 一、显著降低成本以前,企业部署一套CRM系统不仅要投入......
  • JS实现导出Excel的五种方法详解
    <!DOCTYPEhtml><html><headlang="en"><metacharset="UTF-8"><title>html表格导出道</title><scriptlanguage="JavaScript"type="text/javascript">//第一种方法function......
  • SQL server CONVERT()函数关于data用法
    CONVERT()函数是把日期转换为新数据类型的通用函数。CONVERT()函数可以用不同的格式显示日期/时间数据。语法:CONVERT(data_type(length),data_to_be_converted,style)       data_type(length) 规定目标数据类型(带有可选的长度)。data_to_be_converte......
  • Python爬虫:抖音 JS XB逆向解析
    哈喽兄弟们,抖音现在有JS加密,以前的方法爬不了饿了,今天来实现一下某音短视频的JS逆向解析。知识点动态数据抓包`在这里插入代码片`requests发送请求X-Bogus 参数逆向环境模块python 3.8               运行代码pycharm 2022.3           辅......
  • 探索Vue生命周期钩子函数:从创生到销毁
    Vue这个引领前端开发潮流的框架,其优雅的响应式数据绑定和组件式开发方式,使得它备受瞩目。然而,Vue的魅力绝不仅限于此,它还赋予开发者一组神奇的生命周期钩子函数,能够在组件的各个成长阶段插入自定义代码。本文将带你进入Vue生命周期钩子函数的奇妙世界,一探它们的妙用、内部机制,同时......
  • 2023年10月最新全国省市区县和乡镇街道行政区划矢量边界坐标经纬度地图数据 shp geojs
    发现个可以免费下载全国 geojson 数据的网站,推荐一下。支持全国、省级、市级、区/县级、街道/乡镇级以及各级的联动数据,支持导入矢量地图渲染框架中使用,例如:D3、Echarts等geojson数据下载地址:https://geojson.hxkj.vip该项目github地址:https://github.com/TangSY/echarts-m......
  • 设计模式01 —— 设计模式简介
    设计模式01——设计模式简介本教程参考:菜鸟教程-学的不仅是技术,更是梦想!(runoob.com)为本人学习笔记,和课程学习笔记,希望各位大佬多多指点!设计模式的简介设计模式可以看作一套被人反复使用的,多人知晓的代码设计的经验总结。设计模式是软件工程的基石。以下是完全版:设......
  • JS 实现模拟键盘事件
    //获取事件需要绑定的节点varinp=document.getElementById('id')//创建初始化event事件varevent=newKeyboardEvent("keyup",{which:13,keyCode:13,key:'Enter',code:'Enter'});//执行inp.dispatchEvent(event) 参考:https://develo......
  • Security Reduction学习笔记(2):预备知识(群环域,双线性配对,哈希函数)
    省略部分可参考密码协议学习笔记(1.4):密码学的一些数学基础-Isakovsky-博客园(cnblogs.com)有限域:$\mathbb{F}$是有限个元素的集合若$(\mathbb{F},+,*)$满足某些条件(条件略),则称其为有限域(FiniteField,或称Galois域)其零元,单位元分别记为$0_{\mathbb{F}},1_{\mat......