学习JavaScript设计模式之状态模式

以下是详细的攻略:

学习JavaScript设计模式之状态模式

状态模式是什么?

状态模式是一种行为型设计模式,它允许对象在其内部状态更改时更改其行为。这个模式的主要想法是通过创建有限状态机来满足状态相关行为的需要。

状态模式的优点?

  • 优化大型、复杂代码的结构。
  • 减少了 if 语句的使用,使得代码更加简洁。
  • 更好的可扩展性,可以方便地增加、删除或修改状态,而无需更改上下文代码。
  • 状态类可以自行管理其状态,从而有助于减少上下文的复杂性。

状态模式的组成部分?

  • State(状态):定义有限状态机中的状态,每个状态都实现自己的行为。
  • Context(上下文):维护着一个 State 实例并将所有与状态相关的请求委托给当前状态。
  • ConcreteState(具体状态):实现状态定义的行为。

状态模式的实现过程?

我们将使用一个由闹钟组成的示例来说明状态模式。

  1. 首先,定义一个 State 接口或抽象类,该接口或类声明了所有具体状态类所需实现的操作:
class State {
  constructor(clock) {
    this.clock = clock;
  }

  clickMode() {
    throw new Error('This method must be overwritten!');
  }

  clickH() {
    throw new Error('This method must be overwritten!');
  }

  clickM() {
    throw new Error('This method must be overwritten!');
  }
}
  1. 然后,定义所有具体状态类都需实现的操作:
class ClockState extends State {
  clickMode() {
    this.clock.setState(new AlarmState(this.clock));
  }

  clickH() {
    throw new Error('This method must be overwritten!');
  }

  clickM() {
    throw new Error('This method must be overwritten!');
  }
}

class DisplayState extends State {
  clickMode() {
    this.clock.setState(new ClockState(this.clock));
  }

  clickH() {
    this.clock.incrementHour();
  }

  clickM() {
    this.clock.incrementMinute();
  }
}

class AlarmState extends State {
  clickMode() {
    this.clock.setState(new DisplayState(this.clock));
  }

  clickH() {
    this.clock.incrementAlarmHour();
  }

  clickM() {
    this.clock.incrementAlarmMinute();
  }
}
  1. 接着,定义上下文(闹钟)类,维护一个 State 实例并将所有与状态相关的请求委托给当前状态:
class Clock {
  constructor() {
    this.state = new DisplayState(this);
    this.hour = 0;
    this.minute = 0;
    this.alarmHour = 0;
    this.alarmMinute = 0;
  }

  setState(state) {
    this.state = state;
  }

  incrementHour() {
    this.hour++;
    console.log(`Hours: ${this.hour}`);
  }

  incrementMinute() {
    this.minute++;
    console.log(`Minutes: ${this.minute}`);
  }

  incrementAlarmHour() {
    this.alarmHour++;
    console.log(`Alarm hours: ${this.alarmHour}`);
  }

  incrementAlarmMinute() {
    this.alarmMinute++;
    console.log(`Alarm minutes: ${this.alarmMinute}`);
  }

  mode() {
    this.state.clickMode();
    console.log(`Current state: ${this.state.constructor.name}`);
  }

  h() {
    this.state.clickH();
  }

  m() {
    this.state.clickM();
  }
}
  1. 最后,使用上下文类实例来启动整个程序:
const clock = new Clock();
clock.h();
clock.m();
clock.mode();
clock.m();
clock.h();
clock.mode();
clock.mode();
clock.h();

这个示例充分展示了状态模式的能力:通过使用基类和 ConcreteState 向对象中添加新的状态并将其轻松重新配置,同时保持开放/封闭原则,即不会修改现有代码。

示例说明:

一个本地存储库示例说明了状态模式的优点。这个库有两个状态:

  1. 未初始化状态。
  2. 初始化状态。

当状态处于未初始化状态时,LocalStore 类的各种方法将不执行。当状态转换为初始化状态时,这些方法将执行,并且检查是否存在本地存储,如果它不存在,则进行初始化。

class LocalStoreUninitialized {
  constructor() {
    console.log('store is uninitialized');
  }

  init() {
    console.log('Initializing store...');
    const items = {};
    localStorage.setItem('items', JSON.stringify(items));
    this.localStore.setState(this.localStore.states.initialized);
  }

  addNew(item) {
    throw new Error('Cannot call addNew() on an uninitialized store.');
  }

  deleteItem(id) {
    throw new Error('Cannot call deleteItem() on an uninitialized store.');
  }

  getItems() {
    throw new Error('Cannot call getItems() on an uninitialized store.');
  }
}

class LocalStoreInitialized {
  constructor() {
    console.log('store is initialized');
  }

  init() {
    console.log('store already initialized');
  }

  addNew(item) {
    const items = JSON.parse(localStorage.getItem('items'));
    items[item.id] = item;
    localStorage.setItem('items', JSON.stringify(items));
    console.log(`Added new item with id: ${item.id}`);
  }

  deleteItem(id) {
    const items = JSON.parse(localStorage.getItem('items'));
    delete items[id];
    localStorage.setItem('items', JSON.stringify(items));
    console.log(`Deleted item with id: ${id}`);
  }

  getItems() {
    const items = JSON.parse(localStorage.getItem('items'));
    console.log(items);
  }
}

class LocalStore {
  constructor() {
    this.states = {
      uninitialized: new LocalStoreUninitialized(),
      initialized: new LocalStoreInitialized(),
    };
    this.setState(this.states.uninitialized);
  }

  setState(state) {
    this.state = state;
    this.state.localStore = this;
  }

  init() {
    this.state.init();
  }

  addNew(item) {
    this.state.addNew(item);
  }

  deleteItem(id) {
    this.state.deleteItem(id);
  }

  getItems() {
    this.state.getItems();
  }
}

const localStore = new LocalStore();

localStore.init();
localStore.addNew({
  id: 1,
  name: 'Item 1',
});
localStore.getItems();
localStore.addNew({
  id: 2,
  name: 'Item 2',
});
localStore.getItems();
localStore.deleteItem(1);
localStore.getItems();

在上面的示例中,我们使用了两个具体状态(初始化和未初始化),以及 LocalStore 类的上下文来管理状态。当 LocalStore 类的实例创建时,它的状态被设置为未初始化。这意味着所有 LocalStore 上的方法都将抛出错误,直到 init 方法被调用并初始化了存储。初始化之后,状态被设置为初始化,并且上下文中的所有方法都可以正常工作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:学习JavaScript设计模式之状态模式 - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • JavaScript数组reduce()方法使用实例详解

    JavaScript数组reduce()方法使用实例详解 在JavaScript中,数组reduce()方法是一种非常有用的方法,它可以将数组中的元素累加到一起,从而得到一个最终的结果。本文将详细介绍reduce()方法的使用方法,并通过示例说明它的用法。 reduce()方法的语法 reduce()方法是数组对象的一个方法,它可以接收两个参数,第一个参数是…

    JavaScript 2023年5月27日
    00
  • 详解JavaScript原型与原型链

    详解JavaScript原型与原型链 前置知识 在深入讲解JavaScript原型与原型链之前,需要了解以下概念: 对象 构造函数 实例 继承 原型 JavaScript中有一个对象,称为原型对象(prototype object),它指向一个JavaScript对象。每个JavaScript对象都有一个原型对象。 在对象定义时,可以通过Object.cre…

    JavaScript 2023年6月10日
    00
  • Vuejs使用addEventListener的事件如何触发执行函数的this

    当我们在Vuejs中使用addEventListener添加事件监听器时,我们需要注意事件处理函数的this指向问题。如果我们使用传统的写法编写事件监听函数,那么this指向的就是监听器所在的DOM元素。在Vuejs中,我们的事件处理函数需要绑定到Vue实例上,这样才能使用Vue实例中的数据和方法。 下面是一些如何解决Vuejs中addEventListen…

    JavaScript 2023年6月11日
    00
  • Vue router配置与使用分析讲解

    对于Vue router配置与使用,可以分为以下几个部分进行讲解: 安装Vue router 配置Vue router 使用Vue router 下面我们逐一讲解。 1. 安装Vue router 首先,我们需要在项目中安装Vue router。可以使用npm或yarn安装。命令如下: npm install vue-router –save 或 yarn…

    JavaScript 2023年6月11日
    00
  • ES6模板字符串和标签模板的应用实例分析

    下面是关于 “ES6模板字符串和标签模板的应用实例分析” 的完整攻略。 ES6模板字符串 ES6模板字符串是一种新的字符串类型,用反引号 (`) 包裹字符串,可以使用 ${} 语法插入变量和表达式。它们也可以跨越多行而不需要添加额外的转义字符。 下面是一个示例,使用ES6模板字符串构建HTML模板: const name = ‘John’; const ag…

    JavaScript 2023年5月28日
    00
  • JavaScript Promise与async/await作用详细讲解

    JavaScript Promise与async/await作用详细讲解 Promise的概念及作用 Promise是ES6引入的一种异步编程的解决方案。Promise可以看作是一种容器,里面保存着一个异步操作的结果。Promise对象有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。当Promise对象的状态从…

    JavaScript 2023年5月28日
    00
  • 原生javascript实现文件异步上传的实例讲解

    原生JavaScript实现文件异步上传可以分为以下几个步骤: 获取上传文件的表单元素,并绑定change事件。在change事件中,获取文件对象并进行处理,最终调用上传函数。 // 获取上传文件表单元素 const fileInput = document.getElementById(‘fileInput’); // 绑定change事件 fileInp…

    JavaScript 2023年5月27日
    00
  • JavaScript接口实现方法实例分析

    JavaScript接口实现方法实例分析 本文介绍了JavaScript接口实现的方法和技巧,同时提供了两个具体的示例说明。 什么是接口 在JavaScript中,接口是一种规范,它定义了对象应该具备的属性和方法,但不给出具体的实现。接口只是提供了一个契约,要求实现它的对象必须按照接口规定的方式来实现。 为什么要使用接口 使用接口可以提高代码的复用性和可维护…

    JavaScript 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部