Simple case of object-oriented and functional programming

Simple case of object-oriented and functional programming

Crazy technology house front-end pioneer

Simple case of object-oriented and functional programming

Introduction


Let me briefly introduce object-oriented and functional programming.

Both are programming paradigms, and they differ in the technology allowed and prohibited.

There are programming languages ​​that support only one paradigm, such as Haskell (purely functional).

There are also languages ​​that support multiple paradigms, such as JavaScript. You can use JavaScript to write object-oriented code or functional code, or even mix the two.

Create project


Before delving into the differences between these two programming paradigms, create a factorial calculator project.

First create all the files and folders you need as follows:


$ mkdir func-vs-oop
$ cd ./func-vs-oop
$ cat index.html
$ cat functional.js
$ cat oop.js 

Next, create a simple form in index.html.


<!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>

To make the interface look less ugly, we use bootstrap as the CSS framework.

If this HTML is displayed in the browser, it should look like this:
Simple case of object-oriented and functional programming

There is no operation on this form yet.

Our goal is to implement a logic in which you can enter a number up to 100. After clicking the "Calculate" button, the result should be displayed in the result-div.

The following are implemented in object-oriented and functional ways.

Functional implementation


First create a file for the functional programming method.


$ cat functional.js

First, we need a function to be called when this file is loaded into the browser.

This function gets the form first, and then adds the function we need to the submission event of the form.


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

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

First declare a function named addSubmitHandler.

This function has two parameters, the first is the tag to be found in HTML, and the second is the function to be bound to the commit-event of the Element.

Next, call this function by passing in #factorial-form and the function name factorialHandler.

The # in front of the tag indicates that we are looking for the id attribute in HTML.

If you try to run the code now, an error will be thrown because the functions getElement and factorialHandler are not defined anywhere.

Therefore, first define getElement before the addSubmitHandler function, as shown below:


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

This function is very simple and only returns the HTML elements found through the passed markup. But we will reuse this feature later.

Now add the factorialHandler function to create the core logic.


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

  const inputNumber = getValueFromElement('#factorial');

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

Call preventDefault immediately after returning the event.

This will prevent the default behavior of the Submit event, you can try what happens when you click the button without calling preventDefault.

After that, the value entered by the user is obtained from the input field by calling the getValueFromElement function. After getting the number, use the function calculateFactorial to calculate the factorial, and then display the result on the page by passing the result to the function displayResult.

If the format of the value is incorrect or the number is greater than 100, an error will be thrown and an alert will pop up.

Next, create two other auxiliary functions: getValueFromElement and displayResult, and add them after the getElement function.


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

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

Both of these functions use our getElement function. This reusability is one reason why functional programming is so effective.

To make it more reusable, you can add a second parameter named tag to the displayResult.

This allows you to dynamically set the elements that should display the results.

But in this example, I used a hard-coded approach.

Next, create the calculateFactorial function in front of the factoryHandler.


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'
    );
  }
}

Then create a function named validate to verify whether the parameter number is empty and not greater than 100, and the type is number. If the check passes, the factorial function is called and the result is returned. If it fails, the error caught in the factorialHandler function is thrown.


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;
  }
}

In this function, switch is used to determine the type of verification paradigm to be executed. This is just a simple value verification.

Then add the actual factor function in front of the calculateFactorial declaration. This is the last function.


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

The final functional.js file is shown below:


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);

In this method, we deal exclusively with functions. Each function has only one purpose, and most functions can be reused in other parts of the program.

For this simple Web program, the functional approach is a bit too much. The same function will be written next, but this time it is object-oriented.

Object-oriented implementation


First, you need to change the src in the script tag of the index.html file to the following.


<script src="oop.js" defer></script>

Then create the oop.js file.


$ cat oop.js

For the object-oriented approach, we have to create three different classes, one for validation, one for factorial calculation, and the other for processing forms.

First, create a class that handles the form.


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();

Get the form-element and input-element in the constructor and store them in class variables (also called attributes). Then add the method factorialHandler to Submit-event. In this case, you need to bind the this of the class to the method. If you don't do this, you will get a reference error. For example, calling this.numberInput.value will be undefined. Then create a class method factorialHandler with the event as a parameter.

The code for this method should look a bit familiar, for example the if statement checks whether the input value is valid, just like it is done in the calculateFactorial function. Validator.validate is a call to the static method in the Validator class that we still need to create. If you use a static method, you do not need to initialize a new instance of the object. After the verification is passed, a new instance of the Factorial class is created, the input value is passed, and the calculated result is displayed to the user.

Next, create the Validator class in front of the InputForm class.


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;
    }
  }
}

Everything inside this class is static, so we don't need any constructor.

The advantage of this is that you don't need to initialize the class every time you use it.

The validate and validate functions are almost identical to our functional.js.

Next, create the Factorial class after the Validator class.


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;
  }
}

After initializing the instance of this class, we obtain the resultElement and store it as an attribute and the number we passed in.

Then call the method calculate and store its return value in the attribute. The calculate method contains the same code as the factor function in functional.js. Finally, there is the display method, which sets the innerHTML of the result element to the actual calculated factorial number.

The complete oop.js file is shown below.


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();

We created three classes to handle three different functions of the program:

  • Validation: Validation class
  • Factorial processing: Factorial class
  • Form processing: InputForm class

    to sum up


Both methods are effective ways to write code. I like to try the most effective method in my different projects. In many cases, it is even impossible to separate the two paradigms so clearly.

Hope this article can give you a basic understanding of different programming methods.

Guess you like

Origin blog.51cto.com/15077562/2609675