Functional vs Object Oriented vs Procedural programming
Tags: javascript, functional, oop, programming
Published: 2021-08-09 17:19:57


## Intro

This is a real life example showing differences of three most common programming paradigms. I will be solving **one** problem in **three** different ways.  
It's based on the [Academind Video](https://www.youtube.com/watch?v=aoE-92Ac4zE)
but in the end my solution happened to vary a bit.

Each example will handle form submit, validate user input and print created _user_ to the console. I also added saving error logger.

## Html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- <script src="procedural.js" defer></script> -->
    <!-- <script src="oop.js" defer></script> -->
    <!-- <script src="functional.js" defer></script> -->
  </head>
  <body>
    <form id="user-form">
      <div>
        <label for="username">Username</label>
        <input id="username" />
      </div>
      <div>
        <label for="password">Password</label>
        <input id="password" type="password" />
      </div>
      <button type="submit">Submit</button>
    </form>
  </body>
</html>
```

Simple HTML login form which will have three valid `js` files in different paradigms.

## Procedural programming

Procedural programming is just solving problem step by step. It's completely valid way of coding, but it has many drawbacks when you want your application to scale.

```js
const form = document.querySelector('form')
const logs = []

form.addEventListener('submit', e => {
  e.preventDefault()
  const username = e.target.elements.username.value
  const password = e.target.elements.password.value

  let error = ''

  if (username.trim().length < 3)
    error = 'Username must be at least 3 characters long'
  else if (!password.match(/[0-9]/))
    error = 'Password must contain at least one digit'

  if (error) {
    logs.push(error)
    alert(error)
    return
  }

  const user = {
    username,
    password,
  }

  console.log(user)
  console.log(logs)
})
```

Simple step by step solution to the problem. But it's not reusable and scalable at all. Although it's completely valid for solving such problem and as you will see it's much shorter than others.

## Object Oriented Programming

Object Oriented Programming (_OOP_) is the closest to the real world so it's quite easy to wrap your mind around. We look at the code dividing it to **Object** where each one does only it's job.

> Useful concept to learn in **OOP** is **SOLID**.

```js
// Class responsible only for logging
class Logger {
  static logs = []

  static showAlert(message) {
    this.logs.push(message)
    alert(message)
  }
}

// Class responsible only for validating input
class Validator {
  static flags = {
    minLength: 'MIN-LENGTH',
    hasDigit: 'HAS-DIGIT',
  }

  static validate(value, flag, validatorValue) {
    if (flag === this.flags.minLength) {
      return value.trim().length >= validatorValue
    }

    if (flag === this.flags.hasDigit) {
      return value.match(/[0-9]/)
    }
  }
}

// Class responsible only for creating valid user
class User {
  constructor(username, password) {
    if (!Validator.validate(username, Validator.flags.minLength, 3))
      throw new Error('Username must be at least 3 characters long')
    if (!Validator.validate(password, Validator.flags.hasDigit))
      throw new Error('Password must contain at least one digit')

    this.username = username
    this.password = password
  }
}

// Class responsible only for from handling
class FormHandler {
  constructor(formElement) {
    this.form = formElement
    this.form.addEventListener('submit', this.handleSubmit.bind(this))
  }

  handleSubmit(e) {
    e.preventDefault()
    const username = e.target.elements.username.value
    const password = e.target.elements.password.value

    try {
      const user = new User(username, password)
      console.log(user)
      console.log(Logger.logs)
    } catch (err) {
      Logger.showAlert(err)
    }
  }
}

const form = document.querySelector('form')
new FormHandler(form)
```

Now you can see what I meant by dividing problem to **Objects**. **FormHandler** is it's own class that takes care of form handing. **User** is another class that takes care of creating user and validates the input using **Validator** class. If theres an error **Logger** class is used to display an alert and save the log.

As you can see there is much more code and it looks more complicated... So why would anyone prefer this over **Procedura** paradigm?

Cool thing is that now we can use it for any similar form just by calling

```js
new FormHandler(new_form)
```

So it's reusable across every file that includes this script. And also it's easily extendable because everything is divided into blocks that do one thing only (_Single responsibility principle_).

## Functional

Finally my favorite paradigm of all. It's really popular for the time of writing this and quite straight forward.

> Note that it doesn't mean that it's better by any means. Which to use is
> completely up to you although some paradigms might be better for certain problem.

```js
const FLAGS = {
  minLength: 'MIN-LENGTH',
  hasDigit: 'HAS-DIGIT',
}

// Function that handles validation
const validate = (value, flag, validatorValue) => {
  switch(flag){
    case FLAGS.minLength:
      return value.trim().length >= validatorValue

    case FLAGS.hasDigit:
      return !!value.match(/[0-9]/)
  }
}

// Function that sets submit handler
const setFormSubmitHandler = (formId, onSubmit) => {
  const form = document.getElementById(formId)
  form.addEventListener('submit', onSubmit)
}

// Function that returns values of required fields as object
// In this case it will return {username: "<value>", password: "<value>"}
// It might look scary but keep in mind that it's completely reusable
const getFormValues = (e, ...fields) => {
  const values = Object.entries(e.target.elements)
  const filteredValues = values.filter(([key]) => fields.includes(key))
  return filteredValues.reduce(
    (acc, [key, { value }]) => ({ ...acc, [key]: value }),
    {}
  )
}

// Function that creates valid user
const createUser = (username, password) => {
  if (!validate(username, FLAGS.minLength, 3))
    throw new Error('Username must be at least 3 characters long')
  if (!validate(password, FLAGS.hasDigit))
    throw new Error('Password must contain at least one digit')

  return { username, password }
}

// Function that creates logger object with *logs* and *showAlert* function
const logger = (() => {
  const logs = []
  return {
    logs,
    showAlert: message => {
      logs.push(message)
      alert(message)
    },
  }
})()

// Main function
const handleSubmit = e => {
  e.preventDefault()
  const { username, password } = getFormValues(e, 'username', 'password')

  try {
    const user = createUser(username, password)
    console.log(user)
    console.log(logger.logs)
  } catch (error) {
    logger.showAlert(error)
  }
}

setFormSubmitHandler('user-form', handleSubmit)
```

As you can see in _Functional programming_ we want to solve the problem using small (ideally _pure_) functions. This method is also very scalable and functions can be reusable. 

> Pure function is a function without a side effects that are hard to track.
> Pure function should only depend on the given arguments.

## Conclusion

There are no better and worse paradigms. Experienced developer can see the advantages of each and chose the best for given problem.

Procedural programming does not say you can't use _functions_ and Functional programing does not prevent you from using _Class_. These paradigms just help to solve the problem in a way that can be beneficial as the code grows.