Object Oriented and Functional Programming
Tags: typescript, oop, functional
Published: 2020-08-01 17:03:18


In our day to day programming, one must follow a certain paradigm
wether its procedural, imperative, object oriented or functional programming.

In this post we will explore **object oriented** and **functional programming**,
explore a simple example on how to implement a simple user story for both paradigms.

I'm no expert in object oriented nor design patterns in OOP, as well as
functional programming. You may find this post opinionated on implementation level.
But I will try my best to express it on how we might code it on our day to day basis.
With all the caveats, let's proceed anyway.

---


### User story

A Customer should have the following information

- firstname
- middlename (not required)
- lastname
- age


Sales clerk should allow view of the following:

**Name: {lastname}, {firstname} {middleInitial}**

or

**Name: {lastname}, {firstname}**

**Status: {Allowed} or {Not Allowed}**

Notes
- Name should have a period at the end, if has a middle name, otherwise none.
- Status allowed if the customer is in legal age.

---


## Object Oriented Programming

OOP encapsulate states in an object, object is created using an instance of class, classes have methods that operates the object states.

``` typescript

class Customer {
	firstname: string
	middlename: string
	lastname: string
	age: number
}

```

Okay, so far so good.

We also want a concatenated information of the customer name,
we should achive this by creating a method that formats the
customer information.

``` diff
class Customer {

...

+	formattedName: string
+	
+	formatCustomerName () {
+		this.formattedName = `${this.lastname}, ${this.firstname}`;
+		if (this.middlename) {
+			if (this.middlename[0]) {
+				this.formattedName = `${this.formattedName} ${this.middlename[0]}.`
+			}	
+		}
+		this.formattedName = 'Name: ' + this.formattedName;
+	}
}
```

and finaly we add status.

``` diff
class Customer {

...
	
+	isLegalAge () {
+		return this.age >= 18;
+	}

+	getStatus (): string {
+		return 'Status: ' + this.isLegalAge() ? 'Allowed' : 'Not Allowed'
+	}

}
```

At this point, we already fulfill the requirement. We can already ship right?

> Wait, What about if we have another format for customer name?

We could add another method to our Customer class, if we do that we end up many formatters in a single class, and the customer has now many responsibility at hand.

So, lets introduce a formatter in strategy pattern.


``` typescript

interface ICustomerFormatter {

	format(customer: Customer): string
}

class SalesClerkCustomerFormatter implements ICustomerFormatter {

	format(customer: Customer): string {
		let formattedName = `${customer.lastname}, ${customer.firstname}`;
		if (customer.middlename) {
			if (customer.middlename[0]) {
				return formattedName = `${formattedName} ${customer.middlename[0]}.`
			}
		}
		return 'Name: ' + formattedName;
	}
}

class ReportingCustomerFormatter implements ICustomerFormatter {

	format(customer: Customer): string {

		throw new Error('not implemented yet');
	}
}
```

``` diff

class Customer {

...

-	formatCustomerName () {
-		this.formattedName = `${this.lastname}, ${this.firstname}`;
-		if (this.middlename) {
-			if (this.middlename[0]) {
-				this.formattedName = `${this.formattedName} ${this.middlename[0]}.`
-			}	
-		}
-		this.formattedName = 'Name: ' + this.formattedName;
-	}
+	formatCustomerName (formatter: ICustomerFormatter) {
+		this.formattedName = 'Name: ' + formatter.format(this);
	}

...

}

```

``` typescript

// Usage

let customer = new Customer();
customer.firstname = 'sansa';
customer.middlename = 'tully';
customer.lastname = 'stark';

customer.formatCustomerName(new SalesClerkCustomerFormatter());
customer.formattedName; // Sales Clerk formatted customer name;

customer.formatCustomerName(new ReportingCustomerFormatter());
customer.formattedName; // Reporting formatted customer name;

```

---

## Functional Programming

Functional programming works on data and pure functions that transforms them.

We could start by defining a record for our customer.

``` typescript

type Customer = {
	firstname: string
	middlename: string
	lastname: string
	age: number
}

```
and we add helper functions.

``` typescript

const formatSalesClerkCustomerName = (customer: Customer): string => {
	const formattedName = `${customer.lastname}, ${customer.firstname}`;
	return (customer.middlename && customer.middlename[0]) ?
		`${formattedName} ${customer.middlename[0]}.` : formattedName;
};

const isCutomerLegalAge = (customer: Customer): boolean => {
	return customer.age > 18;
};

const customerStatus = (customer: Customer): string => {
	return 'Status: ' + isCutomerLegalAge(customer)? 'Allowed' : 'Not Allowed'; 
};

```

That's all we need.

> How about multiple formats for customer name?

We could also have a strategy pattern in FP, we have to use a higher order function for that.


``` typescript

const formatCustomerName = (
	customer: Customer,
	nameFormatter: (customer: Customer) => string): string => {
	return 'Name: ' + nameFormatter(customer);
};

const reportingFormatter = (customer: Customer): string => {
	
	throw new Error('not implemented yet');
};


let bran = {
	firstname = 'bran',
	middlename = 'tully',
	lastname = 'stark',
	age: 8
}

// Usage

formatCustomerName(bran, formatSalesClerkCustomerName);
formatCustomerName(bran, formatReporting);

```

## Conclusion

We saw two different paradigms and example on how its implemented,
Decomposed using **Strategy Pattern**. Choosing paradigm is merley up to you, or your team standard.

I for myself tends to write in both paradigm. When confronted with calculations and working on mapping asynchronous tasks, I leans towards FP. On the other hand, for example Controllers from MVC, then I use OOP and as much as possible using pure functions, on methods.


---

Let me know what you think below.

Source Codes:

[OOP Code](https://gist.github.com/ronnelreposo/8e4d358ddcec7a16efcb14bcd898d15b)

[FP Code](https://gist.github.com/ronnelreposo/8d18a2c4e04d626a593d532740a99f25)