You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript计算器应用中正确应用命令模式?

命令模式在计算器中的具体实现方案

命令模式的核心是将每个操作封装成独立的命令对象,让请求发送者(UI)和请求执行者(核心逻辑)解耦,同时天然支持撤销、重做等功能。结合你的计算器应用,我会一步步拆解实现方式:

1. 定义命令基类与具体命令

首先为每个运算操作(加、减、乘、除、清空)创建独立的命令类,每个命令需实现execute(执行操作)和undo(撤销操作)两个核心方法,同时保存撤销所需的状态:

// 基础命令抽象(统一接口)
class Command {
  execute() {}
  undo() {}
}

// 加法命令
class AddCommand extends Command {
  constructor(core, targetValue) {
    super();
    this.core = core;          // 持有计算器核心实例
    this.targetValue = targetValue; // 要加的数值
    this.previousValue = null; // 记录执行前的结果,用于撤销
  }

  execute() {
    this.previousValue = this.core.currentValue;
    this.core.currentValue += this.targetValue;
  }

  undo() {
    this.core.currentValue = this.previousValue;
  }
}

// 减法命令(同理实现乘、除命令)
class SubtractCommand extends Command {
  constructor(core, targetValue) {
    super();
    this.core = core;
    this.targetValue = targetValue;
    this.previousValue = null;
  }

  execute() {
    this.previousValue = this.core.currentValue;
    this.core.currentValue -= this.targetValue;
  }

  undo() {
    this.core.currentValue = this.previousValue;
  }
}

// 清空命令
class ClearCommand extends Command {
  constructor(core) {
    super();
    this.core = core;
    this.previousValue = null;
  }

  execute() {
    this.previousValue = this.core.currentValue;
    this.core.currentValue = 0;
  }

  undo() {
    this.core.currentValue = this.previousValue;
  }
}

2. 修改CalculatorCore,适配命令模式

改造核心逻辑类,让它不再直接提供add/subtract这类方法,而是改为接收命令对象,并维护命令历史栈实现撤销:

// CalculatorCore.js
class CalculatorCore {
  constructor() {
    this.currentValue = 0;
    this.commandHistory = []; // 存储已执行的命令,用于撤销
  }

  // 执行传入的命令
  executeCommand(command) {
    command.execute();
    this.commandHistory.push(command);
  }

  // 撤销最后一步操作
  undoLast() {
    if (!this.commandHistory.length) return;
    const lastCmd = this.commandHistory.pop();
    lastCmd.undo();
  }

  // 对外暴露当前值供UI获取
  getCurrentValue() {
    return this.currentValue;
  }
}

3. 改造Calculator.jsx,用命令驱动UI操作

在UI组件中,不再直接调用核心类的运算方法,而是创建对应命令对象,交给核心类执行:

// Calculator.jsx
import React, { useState, useRef } from 'react';
import { CalculatorCore, AddCommand, SubtractCommand, ClearCommand } from './CalculatorCore';

const Calculator = () => {
  // 用useRef保存计算器核心实例,避免重复创建
  const calculatorRef = useRef(new CalculatorCore());
  const [displayValue, setDisplayValue] = useState('0');

  // 数字输入逻辑(保留原有逻辑即可)
  const handleNumberInput = (num) => {
    const newDisplay = displayValue === '0' ? num.toString() : displayValue + num;
    setDisplayValue(newDisplay);
  };

  // 加法按钮点击:创建AddCommand并执行
  const handleAdd = () => {
    const targetNum = parseFloat(displayValue);
    const addCmd = new AddCommand(calculatorRef.current, targetNum);
    calculatorRef.current.executeCommand(addCmd);
    setDisplayValue(calculatorRef.current.getCurrentValue().toString());
  };

  // 减法按钮点击:同理创建SubtractCommand
  const handleSubtract = () => {
    const targetNum = parseFloat(displayValue);
    const subtractCmd = new SubtractCommand(calculatorRef.current, targetNum);
    calculatorRef.current.executeCommand(subtractCmd);
    setDisplayValue(calculatorRef.current.getCurrentValue().toString());
  };

  // 清空按钮点击
  const handleClear = () => {
    const clearCmd = new ClearCommand(calculatorRef.current);
    calculatorRef.current.executeCommand(clearCmd);
    setDisplayValue(calculatorRef.current.getCurrentValue().toString());
  };

  // 撤销按钮点击
  const handleUndo = () => {
    calculatorRef.current.undoLast();
    setDisplayValue(calculatorRef.current.getCurrentValue().toString());
  };

  return (
    <div className="calculator">
      <div className="display">{displayValue}</div>
      <div className="button-grid">
        <button onClick={() => handleNumberInput(1)}>1</button>
        <button onClick={() => handleNumberInput(2)}>2</button>
        <button onClick={() => handleNumberInput(3)}>3</button>
        <button onClick={handleAdd}>+</button>
        <button onClick={() => handleNumberInput(4)}>4</button>
        <button onClick={() => handleNumberInput(5)}>5</button>
        <button onClick={() => handleNumberInput(6)}>6</button>
        <button onClick={handleSubtract}>-</button>
        <button onClick={() => handleNumberInput(7)}>7</button>
        <button onClick={() => handleNumberInput(8)}>8</button>
        <button onClick={() => handleNumberInput(9)}>9</button>
        <button onClick={handleUndo}>Undo</button>
        <button onClick={() => handleNumberInput(0)}>0</button>
        <button onClick={handleClear}>C</button>
      </div>
    </div>
  );
};

export default Calculator;

这样做的核心优势

  • 解耦UI与逻辑:UI只需要关心创建对应命令,不用知道运算细节;核心逻辑只处理命令执行,不用管UI触发方式。
  • 轻松实现撤销/重做:通过维护命令历史栈,undo操作只需要调用最后一个命令的undo方法即可,新增重做功能也只需要再维护一个重做栈。
  • 扩展性极强:如果要新增平方、开根号这类操作,只需要新增对应的SquareCommand/SqrtCommand类,完全不需要修改现有核心逻辑和UI代码,符合开闭原则。
  • 支持批量操作:可以将多个命令组合成一个宏命令,实现一键执行多步操作(比如"清空后加10")。

内容的提问来源于stack exchange,提问作者qwfrjk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:19:01