前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面向对象与函数式编程的简单案例

面向对象与函数式编程的简单案例

作者头像
疯狂的技术宅
发布2020-09-14 15:35:48
1.1K0
发布2020-09-14 15:35:48
举报
文章被收录于专栏:京程一灯京程一灯

介绍

先简要介绍一下面向对象和函数式编程。

两者都是编程范式,在允许和禁止的技术上有所不同。

有仅支持一种范式的编程语言,例如 Haskell(纯函数式)。

还有支持多种范式的语言,例如 JavaScript,你可以用 JavaScript 编写面向对象的代码或函数式代码,甚至可以将两者混合。

创建项目

在深入探究这两种编程范式之间的差异之前,先创建一个阶乘计算器项目。

首先创建所需的所有文件和文件夹,如下所示:

代码语言:javascript
复制
$ mkdir func-vs-oop
$ cd ./func-vs-oop
$ cat index.html
$ cat functional.js
$ cat oop.js 

接下来在 index.html 内创建一个简单的表单。

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
  <script src="functional.js" defer></script>
</head>
<body>
  <div class="container mt-5">
    <div class="container mt-3 mb-5 text-center">
      <h2>Functional vs OOP</h2>
    </div>
    <form id="factorial-form">
      <div class="form-group">
        <label for="factorial">Factorial</label>
        <input class="form-control" type="number" name="factorial" id="factorial" />
      </div>
      <button type="submit" class="btn btn-primary">Calculate</button>
    </form>
    <div class="container mt-3">
      <div class="row mt-4 text-center">
        <h3>Result:</h3>
        <h3 class="ml-5" id="factorial-result"></h3>
      </div>
    </div>
  </div>
</body>
</html>

为了使界面看上去不那么丑陋,我们把 bootstrap 作为 CSS 框架。

如果在浏览器中显示这个 HTML,应该是这样的:

现在这个表单还没有任何操作。

我们的目标是实现一种逻辑,在该逻辑中你可以输入一个最大为 100 的数字。单击“Calculate”按钮后,结果应显示在 result-div 中。

下面分别以面向对象和函数式的方式来实现。

函数式实现

首先为函数式编程方法创建一个文件。

代码语言:javascript
复制
$ cat functional.js

首先,需要一个在将此文件加载到浏览器时要调用的函数。

该函数先获取表单,然后把我们需要的函数添加到表单的提交事件中。

代码语言:javascript
复制
function addSubmitHandler(tag, handler) {
  const form = getElement(tag);
  form.addEventListener('submit', handler);
}

addSubmitHandler("#factorial-form", factorialHandler);

首先声明一个名为 addSubmitHandler 的函数。

这个函数有两个参数,第一个是要在 HTML 中查找的标签,第二个是要绑定到 Elementcommit-event 的函数。

接下来,通过传入#factorial-form 和函数名 factorialHandler 来调用此函数。

标签前面的 # 表明我们正在寻找 HTML 中的 id 属性。

如果现在尝试运行该代码,则会抛出错误,因为在任何地方都还没有定义函数 getElementfactorialHandler

因此,首先在 addSubmitHandler 函数前面定义 getElement ,如下所示:

代码语言:javascript
复制
function getElement(tag) {
  return document.querySelector(tag);
}

这个函数非常简单,只返回通过传入的标记找到的 HTML元素。但是稍后我们将重用此功能。

现在添加 factorialHandler 函数来创建核心逻辑。

代码语言:javascript
复制
function factorialHandler(event) {
  event.preventDefault();

  const inputNumber = getValueFromElement('#factorial');

  try {
    const result = calculateFactorial(inputNumber);
    displayResult(result);
  } catch (error) {
    alert(error.message);
  } 
}

把事件传回后立即调用 preventDefault

这将阻止 Submit 事件的默认行为,你可以试试不调用 preventDefault 时单击按钮后会发生什么。

之后,通过调用 getValueFromElement 函数从输入字段中获取用户输入的值。在得到数字后,用函数 calculateFactorial 计算阶乘,然后通过将结果传递给函数 displayResult 将结果展示到页面。

如果该值的格式不正确或者数字大于 100,将会抛出错误并弹出 alert。

下一步,创建另外两个辅助函数:getValueFromElementdisplayResult,并将它们添加到 getElement 函数后面。

代码语言:javascript
复制
function getValueFromElement(tag) {
  return getElement(tag).value;
}

function displayResult(result) {
  getElement('#factorial-result').innerHTML = result
}

这两个函数都使用我们的 getElement 函数。这种可重用性是为什么函数式编程如此有效的一个原因。

为了使它更加可重用,可以在 displayResult 上添加名为 tag 第二个参数。

这样就可以动态设置应该显示结果的元素。

但是在本例中,我用了硬编码的方式。

接下来,在 factoryHandler 前面创建 calculateFactorial 函数。

代码语言:javascript
复制
function calculateFactorial(number) {
  if (validate(number, REQUIRED) && validate(number, MAX_LENGTH, 100) && validate(number, IS_TYPE, 'number')) {
    return factorial(number);
  } else {
    throw new Error(
      'Invalid input - either the number is to big or it is not a number'
    );
  }
}

接着创建一个名为 validate 的函数来验证参数 number 是否为空且不大于 100,且类型为 number。如果检查通过,就调用 factorial 函数并返回其结果。如果没有通过,则抛出在 factorialHandler 函数中捕获的错误。

代码语言:javascript
复制
const MAX_LENGTH = 'MAX_LENGTH';
const IS_TYPE = 'IS_TYPE';
const REQUIRED = 'REQUIRED';

function validate(value, flag, compareValue) {
  switch (flag) {
    case REQUIRED:
      return value.trim().length > 0;
    case MAX_LENGTH:
      return value <= compareValue;
    case IS_TYPE:
      if (compareValue === 'number') {
        return !isNaN(value);
      } else if (compareValue === 'string') {
        return isNaN(value);
      }
    default:
      break;
  }
}

在这个函数中,用 switch 来确定要执行的验证范式类型。这只是一个简单的值验证。

然后在 calculateFactorial 声明的前面添加实际的 factor 函数。这是最后一个函数。

代码语言:javascript
复制
function factorial(number) {
  let returnValue = 1;
  for (let i = 2; i <= number; i++) {
    returnValue = returnValue * i;
  }
  return returnValue;
}

最终的 functional.js 文件下所示:

代码语言:javascript
复制
const MAX_LENGTH = 'MAX_LENGTH';
const IS_TYPE = 'IS_TYPE';
const REQUIRED = 'REQUIRED';

function getElement(tag) {
  return document.querySelector(tag);
}

function getValueFromElement(tag) {
  return getElement(tag).value;
}

function displayResult(result) {
  getElement('#factorial-result').innerHTML = result
}

function validate(value, flag, compareValue) {
  switch (flag) {
    case REQUIRED:
      return value.trim().length > 0;
    case MAX_LENGTH:
      return value <= compareValue;
    case IS_TYPE:
      if (compareValue === 'number') {
        return !isNaN(value);
      } else if (compareValue === 'string') {
        return isNaN(value);
      }
    default:
      break;
  }
}

function factorial(number) {
  let returnValue = 1;
  for (let i = 2; i <= number; i++) {
    returnValue = returnValue * i;
  }
  return returnValue;
}

function calculateFactorial(number) {
  if (validate(number, REQUIRED) && validate(number, MAX_LENGTH, 100) && validate(number, IS_TYPE, 'number')) {
    return factorial(number);
  } else {
    throw new Error(
      'Invalid input - either the number is to big or it is not a number'
    );
  }
}

function factorialHandler(event) {
  event.preventDefault();

  const inputNumber = getValueFromElement('#factorial');

  try {
    const result = calculateFactorial(inputNumber);
    displayResult(result);
  } catch (error) {
    alert(error.message);
  } 
}

function addSubmitHandler(tag, handler) {
  const form = getElement(tag);
  form.addEventListener('submit', handler);
}

addSubmitHandler("#factorial-form", factorialHandler);

在这种方法中,我们专门处理函数。每个函数都只有一个目的,大多数函数可以在程序的其他部分中重用。

对于这个简单的 Web 程序,使用函数式的方法有些过分了。接着将编写相同的功能,只不过这次是面向对象的。

面向对象的实现

首先,需要将 index.html 文件的脚本标签中的 src 更改为以下内容。

代码语言:javascript
复制
<script src="oop.js" defer></script>

然后创建 oop.js 文件。

代码语言:javascript
复制
$ cat oop.js

对于面向对象方法,我们要创建三种不同的类,一种用于验证,一种用于阶乘计算,另一种用于处理表单。

先是创建处理表单的类。

代码语言:javascript
复制
class InputForm {
  constructor() {
    this.form = document.getElementById('factorial-form');
    this.numberInput = document.getElementById('factorial');

    this.form.addEventListener('submit', this.factorialHandler.bind(this));
  }

  factorialHandler(event) {
    event.preventDefault();

    const number = this.numberInput.value;

    if (!Validator.validate(number, Validator.REQUIRED) 
      || !Validator.validate(number, Validator.MAX_LENGTH, 100)
      || !Validator.validate(number, Validator.IS_TYPE, 'number'))
      {
        alert('Invalid input - either the number is to big or it is not a number');
        return;
      }

      const factorial = new Factorial(number);
      factorial.display();
  }
}

new InputForm();

在构造函数中获取 form-elementinput-element 并将其存储在类变量(也称为属性)中。之后将方法 factorialHandler 添加到 Submit-event 中。在这种情况下需要把类的 this 绑定到方法。如果不这样做,将会得到一个引用错误,例如调用 this.numberInput.value 将会是 undefined。之后以事件为参数创建类方法 factorialHandler

该方法的代码看起来应该有点熟悉,例如 if 语句检查输入值是否有效,就像在 calculateFactorial 函数中所做的那样。Validator.validate 是对我们仍然需要创建的 Validator 类中的静态方法的调用。如果使用静态方法,则无需初始化对象的新实例。验证通过后创建 Factorial 类的新实例,传递输入值,然后将计算的结果显示给用户。

接下来在 InputForm 类 前面创建 Validator 类。

代码语言:javascript
复制
class Validator {
  static MAX_LENGTH = 'MAX_LENGTH';
  static IS_TYPE = 'IS_TYPE';
  static REQUIRED = 'REQUIRED';

  static validate(value, flag, compareValue) {
    switch (flag) {
      case this.REQUIRED:
        return value.trim().length > 0;
      case this.MAX_LENGTH:
        return value <= compareValue;
      case this.IS_TYPE:
        if (compareValue === 'number') {
          return !isNaN(value);
        } else if (compareValue === 'string') {
          return isNaN(value);
        }
      default:
        break;
    }
  }
}

这个类内部的所有内容都是静态的,所以我们不需要任何构造函数。

这样做的好处是不需要在每次使用它时都初始化该类。

validatevalidate 函数与我们的 functional.js 几乎完全相同。

接下来在 Validator 类的后面创建 Factorial 类。

代码语言:javascript
复制
class Factorial {
  constructor(number) {
    this.resultElement = document.getElementById('factorial-result');
    this.number = number;
    this.factorial = this.calculate();
  }

  calculate() {
    let returnValue = 1;
    for (let i = 2; i <= this.number; i++) {
      returnValue = returnValue * i;
    }
    return returnValue;
  }

  display() {
    this.resultElement.innerHTML = this.factorial;
  }
}

在初始化这个类的实例后,我们获得 resultElement 并将其存储为属性以及我们传入的数字。

之后调用方法 calculate 并将其返回值存储在属性中。calculate 方法包含与 functional.js 中的 factor 函数相同的代码。最后是 display 方法,该方法将结果元素的 innerHTML 设置为现实计算出的阶乘数。

完整的 oop.js 文件如下所示。

代码语言:javascript
复制
class Validator {
  static MAX_LENGTH = 'MAX_LENGTH';
  static IS_TYPE = 'IS_TYPE';
  static REQUIRED = 'REQUIRED';

  static validate(value, flag, compareValue) {
    switch (flag) {
      case this.REQUIRED:
        return value.trim().length > 0;
      case this.MAX_LENGTH:
        return value <= compareValue;
      case this.IS_TYPE:
        if (compareValue === 'number') {
          return !isNaN(value);
        } else if (compareValue === 'string') {
          return isNaN(value);
        }
      default:
        break;
    }
  }
}

class Factorial {
  constructor(number) {
    this.resultElement = document.getElementById('factorial-result');
    this.number = number;
    this.factorial = this.calculate();
  }

  calculate() {
    let returnValue = 1;
    for (let i = 2; i <= this.number; i++) {
      returnValue = returnValue * i;
    }
    return returnValue;
  }

  display() {
    this.resultElement.innerHTML = this.factorial;
  }
}

class InputForm {
  constructor() {
    this.form = document.getElementById('factorial-form');
    this.numberInput = document.getElementById('factorial');

    this.form.addEventListener('submit', this.factorialHandler.bind(this));
  }

  factorialHandler(event) {
    event.preventDefault();

    const number = this.numberInput.value;

    if (!Validator.validate(number, Validator.REQUIRED) 
      || !Validator.validate(number, Validator.MAX_LENGTH, 100)
      || !Validator.validate(number, Validator.IS_TYPE, 'number'))
      {
        alert('Invalid input - either the number is to big or it is not a number');
        return;
      }

      const factorial = new Factorial(number);
      factorial.display();
  }
}

new InputForm();

我们创建了三个类来处理程序的三个不同的功能:

  • 验证: Validation
  • 阶乘处理: Factorial
  • 表单处理: InputForm

总结

两种方法都是编写代码的有效方法。我喜欢在自己不同项目中尝试最有效的方法。在很多情况下,甚至不可能如此清晰地分离这两种范式。

希望这篇文章可以使你对不同的编程方法有一个基本的了解。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端先锋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 创建项目
  • 函数式实现
  • 面向对象的实现
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档