For one of you who is already aware of what is Factory Pattern, keep on scrolling, I’m sure this article will surprise you.
For others…
Factory Pattern — a type of creational pattern that usually describes common aspects of the entities by creating a base class, and allows sub-classes to inherit its behaviour and alter it.
When do we use it?
Well, we usually use this approach when our entities have a lot in common, and we want to create a base class where we are going to hold everything that is in common.
This is really helpful to make a DRY and clear code base.
This article will include both Javascript and Typescript examples.
Classic Factory
Most iconic factory, described in every book.
There are a few common technics how we can implement Factory Pattern:
- We create a method in our base class and expect that this method is not going to use something that is going to be changed by children as well as the method itself won’t be changed either.
- We create a property in our base class and we expect that sub-class we re-assign this property. This property may equally be used in the base class and sub-class.
- We create a method that we expect to be implemented or completed in siblings. So for example, if we want developers to implement — we define the method and make it throw an error, this is how developers will be forced to implement it.
Imagine that we want to add multiple fiat wallets in the application. For example, we want to implement Euro, Dollar and British Pounds wallets. Let’s think for a second, about what they have in common.
So, we have to have the ability to view our balance, send and receive money, and obviously notify our API about our changes. Judging from this, those things are in common, the only difference between them will be currency name and for example payment gateway.
Let’s get to the code.
Javascript example
Firstly we’re going to implement our base class.
class BaseWallet {
transactions = [];
_currency = null;
_gateway = null;
showBalance() {
this.transactions.reduce((acc, item) => {
if (item.type === 'in') {
return acc + item.amount;
}
return acc - item.amount;
}, 0)
}
sendMoney(amount) {
const transaction = { amount, date: new Date(), type: 'out' };
this.transactions.push(transaction);
this._updateAPI(transaction);
}
receiveMoney(amount) {
const transaction = { amount, date: new Date(), type: 'in' };
this.transactions.push(transaction);
this._updateAPI(transaction);
}
_authenticate() {
new Error('Implement me!');
}
_updateAPI(transaction) {
if (!this._currency || !this._gateway) {
throw new Error('Incorrect usage of Base class!');
}
this._authenticate();
API.Post(`http://${this._gateway}/transactions/${this._currency}`, { transaction });
}
}
I’m trying to cover as many scenarios as possible here, so let’s walk through the code and see what is happening here. Here is a list that is going to be changed in siblings:
- Line 4 and Line 6 — will be used in our subclasses, we need these values to manipulate our URL on Line 42 in
_updateAPI
method. - Line 32 — method
_authenticate
is created to show to developers, that we expect this method to be implemented in our sub-classes, so unless it is implemented, Line 41 will always rise anImplement me
error. - Line 36 — in
_updateAPI
we first check if variables that need to be defined in our children are set, if they are not defined — we throw an error to highlight for the developer that those properties are required. Then we use_authenticate
which needs to be defined in siblings and we use variables that we expect to be defined in our sub-classes.
Now let’s implement our siblings. First our Euro wallet:
class EURWallet extends BaseWallet {
_currency = 'eur';
_gateway = 'mastercard';
_authenticate() {
API.Patch('https://bankofeurope.com/auth');
}
}
And then our Dollar wallet:
class USDWallet extends BaseWallet {
_currency = 'us
_gateway = 'visa';
_authenticate() {
API.Post('https://bankofamerica.com/auth');
}
}
As you in both of the examples above, you see that we are redefining _currency
and _gateway
properties, as well as define _authenticate
method.
And now we can use the in our application:
const usdWallet = new USDWallet();
usdWallet.sendMoney(100);
usdWallet.receiveMoney(10);
Typescript example
Typescript implementation is really close to what you see in Javascript. Let’s start with the creation of our types:
export type Currencies = 'usd' | 'eur' | 'gbp';
export type Gateways = 'visa' | 'mastercard';
export type Types = 'in' | 'out';
export interface Transaction {
amount: number;
date: Date;
type: Types;
}
Now, implement the base class:
import { Currencies, Gateways, Transaction } from "./types";
class BaseWallet {
public transactions: Transaction[] = [];
protected readonly currency: Currencies;
protected readonly gateway: Gateways;
public showBalance(): void {
this.transactions.reduce((acc: number, item: Transaction): number => {
if (item.type === 'in') {
return acc + item.amount;
}
return acc - item.amount;
}, 0)
}
public sendMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'out' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public receiveMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'in' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
protected authenticate(): void | never {
new Error('Implement me!');
}
private updateAPI(transaction): void | never {
if (!this.currency || !this.gateway) {
throw new Error('Incorrect usage of Base class!');
}
API.Post(`http://${this.gateway}/transactions/${this.currency}`, { transaction });
}
}
What we’re interested in here is how we show developers what we expect to do with methods, so for example:
public
andprotected
methods may be redefined in siblingsprivate
methods cannot be redefined in sub-classes- Also, take a look at how we are manipulating with
never
type in Typescript. Ourauthenticate
method returnsvoid | never
, but we are going to modify this in children, to return onlyvoid
Now our sub-classes. Our Euro wallet class:
class EURWallet extends BaseWallet {
protected readonly currency: Currencies = 'eur';
protected readonly gateway: Gateways = 'mastercard';
protected authenticate(): void {
API.Post('https://bankofeurope.com/auth');
}
}
And USDWallet
class:
class USDWallet extends BaseWallet {
protected readonly currency: Currencies = 'usd'
protected readonly gateway: Gateways = 'visa';
protected authenticate(): void {
API.Post('https://bankofamerica.com/auth');
}
}
As you see we redefine properties, method and we also are able to modify return types, in this case, authenticate
method return only void
.
Pros
- Iconic implementation
- Equally readable and efficient
- Can be used with both JavaScript and Typescript
Cons
- Requires additional code, to validate that everything is done correctly
Abstract Class Factory
Typescript allows us to use a wonderful approach with abstract
class. Since this feature is available only in Typescript, you won’t find a JavaScript example here.
So what is an abstract
class in Typescript? A special class that can be used only for inheritance and cannot be used to create a new instance. Allow to implement methods and properties, but also allow to create a method or property skeleton, with abstract
keyword inside a class.
Since this is settled, let’s move on to the implementation:
import { Currencies, Gateways, Transaction } from "./types";
abstract class BaseWallet {
protected abstract readonly currency: Currencies;
protected abstract readonly gateway: Gateways;
protected abstract authenticate(): void;
public transactions: Transaction[] = [];
public showBalance(): void {
this.transactions.reduce((acc: number, item: Transaction): number => {
if (item.type === 'in') {
return acc + item.amount;
}
return acc - item.amount;
}, 0)
}
public sendMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'out' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public receiveMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'in' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
private updateAPI(transaction): void | never {
this.authenticate();
API.Post(`http://${this.gateway}/transactions/${this.currency}`, { transaction });
}
}
As always, let’s walk through the code:
- The first thing that you’re going to notice is Line 3, an example of
abstract
class usage - Next in Lines 4, 6 and 8, you can see how we define a skeleton of the siblings. Basically, we tell Typescript how we expect those properties to look, and Typescript will force developers to implement or assign those properties in siblings.
The implementation of siblings is the same as on Classic Factory implementation.
Pros
- Have all the benefits of Classic Factory
- Requires less code to implement
Cons
- Requires additional knowledge of the Typescript
Implements Interface Factory
Once again we are going to use the feature that is present only in Typescript. It’s called implements
.
So what is implements
?
It’s a tool in Typescript that allow us to validate that the object has the same structure as we described.
Firstly, we need to implement an interface
that will describe a skeleton of our future classes.
Since we are changing our approach, we also need to re-work our logic.
import { Transaction } from "./types";
interface BaseWallet {
transactions: Transaction[];
sendMoney: (amount: number) => void;
receiveMoney: (amount: number) => void;
updateAPI: (transaction: Transaction) => void;
}
As you see, our interface is a little thinner than our base class was before. We no longer need some of the properties that we described previously, for example:
- We don’t need
currency
andgateway
any more, since we are going to describe it manually - We no longer need
authenticate
function because we are going to implement it manually for each class
Now let’s implement our Dollar wallet:
class USDWallet implements BaseWallet {
public transactions: Transaction[] = [];
public showBalance(): void {
this.transactions.reduce((acc: number, item: Transaction): number => {
if (item.type === 'in') {
return acc + item.amount;
}
return acc - item.amount;
}, 0)
}
public sendMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'out' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public receiveMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'in' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public updateAPI(transaction): void {
API.Post('https://bankofamerica.com/auth');
API.Post(`http://visa.com/transactions/usd`, { transaction });
}
}
And our Euro wallet:
class EURWallet implements BaseWallet {
public transactions: Transaction[] = [];
public showBalance(): void {
this.transactions.reduce((acc: number, item: Transaction): number => {
if (item.type === 'in') {
return acc + item.amount;
}
return acc - item.amount;
}, 0)
}
public sendMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'outcome' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public receiveMoney(amount): void {
const transaction: Transaction = { amount, date: new Date(), type: 'income' };
this.transactions.push(transaction);
this.updateAPI(transaction);
}
public updateAPI(transaction): void {
API.Post(`http://visa.com/transactions/usd`, { tx: transaction });
}
}
Now let’s sum up everything.
As you see, we need to describe every method manually, which basically leads to a lot of code duplication. But on the opposite side of this argument, you may find that methods are different and you don’t need to follow super class rules. And Typescript will help you to validate that class has the required structure.
Pros
- Flexible approach
- It is useful when you want to keep a single access interface for calling instance methods
Cons
- Requires to write more code or include other approaches to make your code base DRY
- You cannot inherit static methods, since
interface
doesn’t allow you to have one
Functional Factory
At some point you will ask yourself, is it even possible to take an OOP approach and use it in functional programming?
So, the cool thing about JavaScript is that it is a multi-paradigm programming language, and allows to use of both OOP and functional approaches.
And there is plenty of cases when OOP is not just suitable for what we are doing, so the question is: Is there a way to apply the Factory pattern in functional programming?
Yes, there is.
So let’s make another example:
We have an application that communicates with crypto-exchange and we need to receive an update through WebSockets. We need to subscribe to different channels, for example, one channel will be orderbook and the second one will be tickers. But we need to have the same interface to subscribe and unsubscribe.
JavaScript example
function wsCommunicationFactory(event, onEvent) {
function subscribe() {
WebsocketsService.subscribe(event, payload => {
onEvent(payload);
});
}
function unsubscribe() {
WebsocketsService.unsubscribe();
}
return [subscribe, unsubscribe];
}
As you see here, we create a function factory that generates two functions that we can use. Keep in mind that we return tuple
, in case we would like to rename results.
event
— will be the name of WebSockets channel, in our case, it can beorderbook
orticker
onEvent
— will be a handler when we receive sockets update
Typescript example
type Subscribe = () => void;
type Unsubscribe = () => void;
type Events = 'orderbook' | 'ticker';
type OnEvent = (payload: object) => void;
function wsCommunicationFactory(event: Events, onEvent: OnEvent): [Subscribe, Unsubscribe] {
function subscribe(): void {
WebsocketsService.subscribe(
event,
(payload: object): void => {
onEvent(payload);
}
);
}
function unsubscribe(): void {
WebsocketsService.unsubscribe(event);
}
return [subscribe, unsubscribe];
}
And this is how to use it:
const [orderbookSubscribe, orderbookUnsubscribe] = wsCommunicationFactory(
'orderbook',
(payload: object) => updateOrderbookUI(payload),
);
const [tickerSubscribe, tickerUnsubscribe] = wsCommunicationFactory(
'ticker',
(payload: object) => updateTickerUI(payload),
);
As you see, we generate two functions for each call, and the more boilerplate your project will be, the more useful this approach becomes.
Pros
- The only way to implement a Factory pattern in the functional programming
- Useful when your project is fully standardised and has a single structure
Cons
- Requires a bit of experience from a developer
- Multiple arguments and configurations will make this approach un-supportable
- Complex inner functions will make it hard to read and support