Skip to content

简单工厂模式

实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。

初学者代码毛病

html
<!doctype html>
<html>
  <head>
    <title>计算器</title>
    <script>
      function calculate() {
        // 这样命名是非常不规范的
        var A = document.getElementById('numberA').value;
        var B = document.getElementById('operator').value;
        var C = document.getElementById('numberB').value;
        var D = '';
        // 判断分支,意味着每个条件都要做判断、等于计算机做了三次无用功
        if (B === '+') D = String(parseFloat(A) + parseFloat(C));
        if (B === '-') D = String(parseFloat(A) - parseFloat(C));
        if (B === '*') D = String(parseFloat(A) * parseFloat(C));
        if (B === '/') D = String(parseFloat(A) / parseFloat(C));
        // 如果除数时,客户输入了0怎么办,如果用户输入的是字符符号而不是数字怎么办?
        document.getElementById('result').innerHTML = '结果是: ' + D;
      }
    </script>
  </head>

  <body>
    <label for="numberA">请输入数字A: </label>
    <input type="number" id="numberA" /><br />

    <label for="operator">请选择运算符号(+、-、*、/): </label>
    <input type="text" id="operator" /><br />

    <label for="numberB">请输入数字B: </label>
    <input type="number" id="numberB" /><br />

    <button onclick="calculate()">计算</button>

    <p id="result"></p>
  </body>
</html>

代码规范

html
<!doctype html>
<html>
  <head>
    <title>简易计算器</title>
  </head>

  <body>
    <label for="numberA">数字 A:</label>
    <input type="text" id="numberA" /><br /><br />

    <label for="operator">运算符号 (+、-、*、/):</label>
    <input type="text" id="operator" /><br /><br />

    <label for="numberB">数字 B:</label>
    <input type="text" id="numberB" /><br /><br />

    <button onclick="calculate()">计算</button><br /><br />

    <div id="result"></div>

    <script>
      function calculate() {
        try {
          const numberA = parseFloat(document.getElementById('numberA').value);
          const operator = document.getElementById('operator').value;
          const numberB = parseFloat(document.getElementById('numberB').value);

          let result = '';
          switch (operator) {
            case '+':
              result = (numberA + numberB).toString();
              break;
            case '-':
              result = (numberA - numberB).toString();
              break;
            case '*':
              result = (numberA * numberB).toString();
              break;
            case '/':
              if (numberB !== 0) result = (numberA / numberB).toString();
              else result = '除数不能为 0';
              break;
          }

          document.getElementById('result').textContent = '结果是:' + result;
        } catch (error) {
          document.getElementById('result').textContent = '您的输入有错:' + error.message;
        }
      }
    </script>
  </body>
</html>

所有编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实是用计算机的方式去思考,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断选择如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护,不容易扩展,更不容易复用。从而达不到高质量代码的要求。

业务的封装

html
<!doctype html>
<html>
  <head>
    <title>计算器</title>
  </head>
  <body>
    <label for="numberA">请输入数字 A:</label>
    <input type="text" id="numberA" /><br />
    <label for="operate">请选择运算符号 (+、-、*、/):</label>
    <input type="text" id="operate" /><br />
    <label for="numberB">请输入数字 B:</label>
    <input type="text" id="numberB" /><br />
    <button onclick="calculate()">计算</button>
    <p id="result"></p>

    <script>
      class Operation {
        static GetResult(numberA, numberB, operate) {
          let result = 0;
          switch (operate) {
            case '+':
              result = numberA + numberB;
              break;
            case '-':
              result = numberA - numberB;
              break;
            case '*':
              result = numberA * numberB;
              break;
            case '/':
              result = numberA / numberB;
              break;
          }
          return result;
        }
      }

      function calculate() {
        try {
          const numberA = parseFloat(document.getElementById('numberA').value);
          const operate = document.getElementById('operate').value;
          const numberB = parseFloat(document.getElementById('numberB').value);

          const result = Operation.GetResult(numberA, numberB, operate);
          document.getElementById('result').textContent = '结果是:' + result;
        } catch (error) {
          document.getElementById('result').textContent = '您的输入有错:' + error.message;
        }
      }
    </script>
  </body>
</html>

当代代码中重复的代码多到一定程度,维护的时候可能就是一场灾难。越大的系统,这种方式带来的问题越严重,编程有一个原则,就是用尽可能的办法去避免重复。让业务逻辑与界面逻辑分开,让它们之间的耦合度下降。只有分离开,才可以达到容易维护或扩展。这样就完全把业务和界面分离了。

紧耦合 vs 松耦合

html
<!doctype html>
<html>
  <head>
    <title>简易计算器</title>
  </head>

  <body>
    <h1>简易计算器</h1>

    <label for="numberA">数字A:</label>
    <input type="number" id="numberA" /><br />

    <label for="numberB">数字B:</label>
    <input type="number" id="numberB" /><br />

    <label for="operation">运算符:</label>
    <select id="operation">
      <option value="add">加法</option>
      <option value="subtract">减法</option>
      <option value="multiply">乘法</option>
      <option value="divide">除法</option></select
    ><br />

    <button onclick="calculate()">计算</button>

    <h2>计算结果:</h2>
    <div id="result"></div>

    <h2>错误消息:</h2>
    <div id="error"></div>

    <script>
      class Operation {
        constructor() {
          this._numberA = 0;
          this._numberB = 0;
        }

        get NumberA() {
          return this._numberA;
        }

        set NumberA(value) {
          this._numberA = value;
        }

        get NumberB() {
          return this._numberB;
        }

        set NumberB(value) {
          this._numberB = value;
        }

        GetResult() {
          let result = 0;
          return result;
        }
      }

      class OperationAdd extends Operation {
        GetResult() {
          let result = this.NumberA + this.NumberB;
          return result;
        }
      }

      class OperationSub extends Operation {
        GetResult() {
          let result = this.NumberA - this.NumberB;
          return result;
        }
      }

      class OperationMul extends Operation {
        GetResult() {
          let result = this.NumberA * this.NumberB;
          return result;
        }
      }

      class OperationDiv extends Operation {
        GetResult() {
          if (this.NumberB == 0) return '除数不能为0。';
          let result = this.NumberA / this.NumberB;
          return result;
        }
      }

      function calculate() {
        var numberA = parseFloat(document.getElementById('numberA').value);
        var numberB = parseFloat(document.getElementById('numberB').value);
        var operation = document.getElementById('operation').value;
        var result;
        var errorMessage = '';

        switch (operation) {
          case 'add':
            var operationAdd = new OperationAdd();
            operationAdd.NumberA = numberA;
            operationAdd.NumberB = numberB;
            result = operationAdd.GetResult();
            break;

          case 'subtract':
            var operationSub = new OperationSub();
            operationSub.NumberA = numberA;
            operationSub.NumberB = numberB;
            result = operationSub.GetResult();
            break;

          case 'multiply':
            var operationMul = new OperationMul();
            operationMul.NumberA = numberA;
            operationMul.NumberB = numberB;
            result = operationMul.GetResult();
            break;
          case 'divide':
            var operationDiv = new OperationDiv();
            operationDiv.NumberA = numberA;
            operationDiv.NumberB = numberB;
            result = operationDiv.GetResult();
            break;

          default:
            errorMessage = '未知操作符';
            result = null;
            break;
        }

        if (result !== null) {
          document.getElementById('result').textContent = '计算结果:' + result;
          document.getElementById('error').textContent = '';
        } else {
          document.getElementById('result').textContent = '';
          document.getElementById('error').textContent = '错误消息:' + errorMessage;
        }
      }
    </script>
  </body>
</html>

简单工厂模式

html
<!doctype html>
<html>
  <head>
    <title>简易计算器</title>
    <script>
      // JavaScript 代码开始
      class Operation {
        constructor() {
          this._numberA = 0;
          this._numberB = 0;
        }

        get NumberA() {
          return this._numberA;
        }

        set NumberA(value) {
          this._numberA = value;
        }

        get NumberB() {
          return this._numberB;
        }

        set NumberB(value) {
          this._numberB = value;
        }

        GetResult() {
          let result = 0;
          return result;
        }
      }

      class OperationAdd extends Operation {
        GetResult() {
          let result = this.NumberA + this.NumberB;
          return result;
        }
      }

      class OperationSub extends Operation {
        GetResult() {
          let result = this.NumberA - this.NumberB;
          return result;
        }
      }

      class OperationMul extends Operation {
        GetResult() {
          let result = this.NumberA * this.NumberB;
          return result;
        }
      }

      class OperationDiv extends Operation {
        GetResult() {
          if (this.NumberB === 0) {
            throw new Error('除数不能为0。');
          }
          let result = this.NumberA / this.NumberB;
          return result;
        }
      }

      class OperationFactory {
        static createOperate(operate) {
          let oper = null;
          switch (operate) {
            case '+':
              oper = new OperationAdd();
              break;
            case '-':
              oper = new OperationSub();
              break;
            case '*':
              oper = new OperationMul();
              break;
            case '/':
              oper = new OperationDiv();
              break;
          }
          return oper;
        }
      }

      function calculate() {
        let numberA = parseFloat(document.getElementById('numberA').value);
        let numberB = parseFloat(document.getElementById('numberB').value);
        let operator = document.getElementById('operator').value;

        let oper = OperationFactory.createOperate(operator);
        oper.NumberA = numberA;
        oper.NumberB = numberB;

        try {
          let result = oper.GetResult();
          document.getElementById('result').textContent = '结果:' + result;
        } catch (error) {
          document.getElementById('result').textContent = '错误:' + error.message;
        }
      }
      // JavaScript 代码结束
    </script>
  </head>
  <body>
    <h1>简易计算器</h1>
    <label for="numberA">数字 A:</label>
    <input type="number" id="numberA" /><br />
    <label for="numberB">数字 B:</label>
    <input type="number" id="numberB" /><br />
    <label for="operator">操作符:</label>
    <select id="operator">
      <option value="+">加法</option>
      <option value="-">减法</option>
      <option value="*">乘法</option>
      <option value="/">除法</option></select
    ><br />
    <button onclick="calculate()">计算</button>
    <p id="result"></p>
  </body>
</html>