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.
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.
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.
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.
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');
}
}
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);
}
...
}
// 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.
type Customer = {
firstname: string
middlename: string
lastname: string
age: number
}
and we add helper functions.
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.
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:
Top comments (0)