If your team is used to functional programming, then know that design patterns like the Strategy pattern can be used in a functional way as well.
The Strategy Pattern is usually implemented using classes. However, it can easily be achieved in a functional pattern.
Normally all strategy objects with the same interface can easily be swapped as needed. This is one of the main benefits of the stategy pattern.
For example, if you wanted to send an email, but wanted to implement a way to change between sending the email via Sparkpost or through AWS, you would do something like the following:
class EmailStrategy {constructor() {}execute(sender, receiver, subject, body) {throw new Error('Not implemented');}}
class SparkPostEmailStrategy extends EmailStrategy {constructor(apiKey, SparkPost) {this.sparkpost = new SparkPost(apiKey);}execute(sender, receiver, subject, body) {return this.sparkpost.transmissions.send({content: {from: sender,subject,html: body,},recipients: [{address: sender}]});}}
class AWSEmailCommand extends IEmailCommand {constructor(AWS) {this.ses = new AWS.SES();}execute(sender, receiver, subject, body) {return new Promise((resolve, reject) => {this.ses.sendEmail({Destination: {ToAddresses: [receiver]},Message: {Body: {Html: {Data: body,},},Subject: {Data: subject,}},Source: sender,}, (err, data) => {if (err) reject(err);resolve(data);});});}}
Now we can use the SparkPostEmailCommand
and the AWSEmailCommand
interchangeably:
const command = new SparkPostEmailCommand(myApiKey, SparkPost);command.execute('[email protected]', '[email protected]', title, body).then((data) => console.log('success')).catch((err) => console.error('failure', err));
But most Command objects are just two methods: a constructor and an execute method (and sometimes an undo method). It almost feels like when we call the constructor, we are just delaying the execution of the execute
method until we want it do perform its side-effect.
To me, this would be a great time to use higher order functions to simplify the code above and remove the need for classes in favor of simple functions.
Since most of the time, all we care about is setting up some dependencies, and then delaying execution, we could just write our Command classes as functions:
const sparkPostEmailCommand = (apiKey, SparkPost) => {const sparkpost = new SparkPost(apiKey);return (sender, receiver, subject, body) => {return sparkpost.transmissions.send({// Same as above});};};
const awsEmailCommand = (AWS) => {const ses = new AWS.SES();return (sender, receiver, subject, body) => {return new Promise((resolve, reject) => {// Same as above});};};
And now, instead of creating new objects, we can just use the command as functions:
sparkPostEmailCommand(myApiKey, SparkPost)('[email protected]', '[email protected]', title, body).then((data) => console.log('success')).catch((err) => console.error('failure', err));
Or we can delay execution:
const execute = sparkPostEmailCommand(myApiKey);// do some workexecute('[email protected]', '[email protected]', title, body);
While classes in other languages would give you some type hinting and safety, the same isn’t true in JavaScript. You can’t really know that all the signatures for your execute
methods all look the same unfortunately.
And without the typing, there’s no real need to add all the bloat that comes with the class implementation. Why worry about the this
binding? Or the extends BaseCommand
?
The point of using these design patterns isn’t that they need to be object oriented. It’s that they help you organize your code using common patterns. Building an object with dependencies and then doing something is a common pattern, where “doing something” may be done in different ways.
If your team is used to classes and objects, then use classes and objects.
If your team is used to functional programming, then know that design patterns like the Command pattern can be used in a functional way as well.
Ivan Montiel is the founder and CEO of Clarity Hub — a company that integrates with Intercom to give your Customer Success team real-time suggestions when helping customers.
You can follow him on Twitter.
.