POSA: design patterns with concrete sample code in Javascript/Typescript

POSA: design patterns with concrete sample code in Javascript/Typescript

With or without your realize, your everyday code is using one or many design patterns which are going to be introduced in this post.

If you google for Design Patterns, there will be bunch of blog posts, books talk about this topic. The recommended book is the canonical Design Patterns Elements of Reusable Object-Oriented Software. Another recommended book is Code Complete: A Practical Handbook of Software Construction, Second Edition 2nd Edition. Pattern-oriented Software Architecture, Patterns for Concurrent and Networked Objects, volume 2.

This post I will briefly list all patterns mentioned in this book and provide a simple explanation/concrete example for each pattern

First, list of design patterns by their purpose and scope

For a better readability, I will use typescript for the sample code.
I will also try to keep the sample code as clean as possible to emphasize the pattern's purpose.

1. Factory method (another name: Virtual Constructor)

create class based on input parameter

const createAnimalClass = (kind: 'dog' | 'cat') => kind === 'dog' ? Dog : Cat
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

2. Adapter (class, another name: Wrapper)

There is an existing class, you want to use it but the interface is not compatible, thus, you need an adapter/wrapper to create compatibility.
For example

class Logger {
    writeLog(str: string){}
class ConsoleLogger {
    log(kind: 'warn' | 'error' | 'log', str: string){}
class ConsoleLoggerAdapter {
    constructor(){this.consoleLogger = new ConsoleLogger()}
    writeLog(str: string){this.consoleLogger.log('log', str)}

Currently, there are many locations in old code base which use interface of Logger, now you migrate your system to use ConsoleLogger but the interfaces are incompatible. ConsoleLoggerAdapter is written to wrap new ConsoleLogger and expose the interface as you want.

3. Interpreter

A class which can interpret a language and store the input string in an internal state instead of the raw input.
Consider regular expression is a language, then RegExp is an interpreter.

const regExp = new RegExp('(0|[1-9][0-9]*)')

4. Template method

Super class (parent class) defines skeleton of an operation which, the operation, includes one or more sub-operations. These sub-operations can be defined/re-defined by sub-classes. The skeleton keeps same regardless the sub-class implementation of the overwritten sub-operations.

class Buyer {
	const unitPrice = 10
    calculateTotal(amount: number){
    	return `the total is ${this.discount(amount * this.unitPrice)}`
class Agency extends Buyer {
	discount(total: number){return total * 0.9 * 1.1} //discount and tax
class Guest extends Buyer {
	discount(total: number){return total * 1.1} //tax

5. Abstract Factory (another name: Kit)

Similar to 1. Factory method. This pattern is used to initiate an instance from a class among a family of them, based on the parameter.

const createAnimal = (kind: 'dog' | 'cat', name: string) => kind === 'dog'
  ? new Dog(name)
  : new Cat(name)

6. Builder

A class which is used to create instances of another class. However, this builder class provides many convenient methods to build the final instance step-by-step, and it remembers each method call internally to give the final or currently being built instance.

class StringBuilder {
	getString(): string
    reset(str: string): StringBuilder
    append(str: string): StringBuilder
    prepend(str: string): StringBuilder
    replace(str: string): StringBuilder
    space(str: string): StringBuilder
new StringBuilder().reset('hello').space().append('world').getString()

7. Prototype

Use an object keeping behaviors (methods) as a prototypical instance. Every time new object is created, the new object copies behaviors from the prototypical instance. This prototype pattern is supported native in Javascript and it brings many strength to the Javascript language itself. Object.create method takes the first parameter as prototype and instantiate new object.

class Human{name: string}
const me = Object.create(Human.prototype, {name: 'transang'})

8. Singleton

Ensure at most one instance of a class can be initiated.

let instance
const getMyClassInstance = () => instance || (instance = new MyClass())

9. Adapter (object)

Same as Adapter for class

10. Bridge (other names: Handle, Body)

Bridge design pattern is used to separate implementation and interface. One of the most application of this pattern is that it enable the implementation can be changed at runtime.
This pattern is similar but different from inheritance pattern.

class Human{
	const pet: IPet
	setPet(pet: IPet): void
	feed(amount: number){this.pet.eat(amount)}
interface IPet {eat(amount: number): void}
class Dog implements IPet {}
class Cat implements IPet {}

The Human class keeps responsibility as a bridge class, separates concern of the interface IPet from its implementations ( Cat and Dog).

Difference from inheritance pattern:
In inheritance pattern, you would need to fix the implementation of IPet in the Human class, which make difficulty to extend the IPet interface, or add a new implementation.

11. Composite

Composite pattern is used to represent part-whole hierarchies.

Image that you want to build a tree whose node can be a leaf or a parent of one or more children nodes. An real-life example of this tree structure is a menu with multi-level sub menus.
A left node is called individual object, while a middle node (can be the root node) composes multiple children, thus, is called composition.

Composite pattern helps treating individual objects and compositions uniformly, in order to avoid defining different classes for each. To achieve this purpose, the composite class should introduce an interface which allow the individual objects directly implementing the interface while the compositions forwards operations to their children.

class Menu { // the general object which exposes interface for both individuals and composites to follow
	draw(): void
class CompositeMenu { // composite object which contains and forwards operations to its children
    	for (const child of this.children) child.draw()
class TextOption extends Menu { // individual object which directly implement menu operations
class ButtonOption extends Menu { // similar to TextOption

12. Decorator (another name: Wrapper)

Decorator pattern attaches additional behavior, functionality to an existing object. It is a flexible replacement for sub-classing.

Object in the above statement can be instance of a class or a class itself.

let car = new Car()
car = attachHandler(car)
car = addWheels(car)
car = fillGasoline(car)

Another example for class decorator

class Printer {
	print(kind: 'error' | 'warning' | 'log', str: string)
const setPrintType = (kind: 'error' | 'warning' | 'log') => (PrinterClass: typeof Printer) => {
	class DecoratedPrinter extends PrinterClass {
    	print(str: string){super.print(kind, str)}
const LogPrinter = setPrintType('log')(Printer)
const WarningPrinter = setPrintType('warning')(Printer)
const ErrorPrinter = setPrintType('error')(Printer)

13. Facade (or Façade)

Image from the book by FoG

Facade pattern helps your life easier by providing a simple and user-friendly API hub for sub-system user without knowledge about the sub-system's internal process.

One of the best example is jQuery. It wraps DOM element and provide more user-friendly API interface and handle all DOM operations and browser compatibility internally.


14. Flyweight

Flyweight pattern shares object in order to save memory or cache(memoize) object to prevent re-creation, re-calculation.

const flyweight = (intensiveCalc: (arg: any) => any) => {
	const results = new Map()
    return (arg: any) => {
    	if (results.has(arg)) return results.get(arg)
        const result = intensiveCalc(arg)
        results.set(arg, result)
        return result

The above implementation considers intensiveCalc is a function which accepts only one argument, it also memoizes all entered parameters and returned result. Thus, it may caught memory overflow. Use it at your own risk.

15. Proxy (another name: Surrogate)

As the name suggested, proxy is a placeholder to control other object. Proxy is a very general concept, used in many other fields such as network connection, ...

class FileProxy{
    	//check permission
        //order a system call to read file
    write(){ // check permission, order a system call to write file

The File class itself can be considered as a proxy for file object in OS. The file object in OS may be another proxy which exposes interface to connect user call and real data stored in hard disk.

16. Chain of Responsibility

Allow more handlers can handle the object. List of handlers should be able to be specified dynamically at run-time.

The syntax looks similar to decorator, but they serves different purposes. Best suite sample is the Promise object

Promise.resolve(3).then(x => x + 2).then(x => x - 3).then(console.log)

17. Command (other names: Action, Transaction)

Encapsulate a request as an object, help decoupling object which knows how to call a command, and, object which handle how to execute the command.
This pattern is also able to store list of executed commands, support undoable operations.

class Dispatcher{
	dispatch(action: IAction): void
    undo(): void
    listActions(): IAction[]

18. Iterator (another name: cursor)

A way to access the elements of an aggregate object (list, array, map, set, ...) without exposing the underlying representation.

const it = [1, 2, 3][Symbol.iterator]()
while (true) {
	const {value, done} = it.next()
    if (!done) break

19. Mediator

A bandmaster who stays in the center to encapsulate the interaction between a set of components. Member component directly communicates with the mediator, instead of talking with other components.

class Mediator {
	const boss: IHuman
    const pets: IPet[]
    const storage: IFood[]
    	//tell this.boos to take food from this.storage, then feed this.pets

Compared to Façade:

  • Façade only exposes existing functions
  • Mediator add more logic (functions) to the system
  • Mediator is more like a controller in MVC pattern, while Façade is merely a way to hide method names behind an interface.
  • Façade is a structural pattern, Mediator is a behavioral pattern.

20. Memento (another name: Token)

Memento pattern includes three components: originator, caretaker, and memento.

Caretaker: a subject (usually our application itself) wants to do some operations on an originator, which changes the originator's internal state. And the caretaker also want to restore the originator's state latter.

Originator: an object (usually a target class) is target of the caretaker, which has its own internal state

Memento: like a snapshot of originator's state, which helps protect the originator's encapsulation in design. Restoring/storing the originator's state must be done via memento.

The original design requires

  • only the originator that produced the memento would be permitted to access the memento's internal state
  • the caretaker can read meta information of the memento object (such as creation time, operation name, ...). However, the caretaker should not access the memento object's state directly.
class Originator {
	class Memento {
    	value: number
    	constructor(value: number){this.value = value}
        getValue(){return value}
    value = 0
    add(amount: number){this.value += amount}
    subtract(amount: number){this.value -= mount}
    restore(memento: Memento){this.value = memento.getValue()}
    store(): Memento{return new Memento(this.value)}

//caretaker is our application
const originator = new Originator()
const memento = originator.store()

21. Observer (other names: Dependents, Publish-Subscribe)

Kindly well-known pattern in Javascript. There is a popular RxJs library which implement many features around observer pattern. Currently, Observable is in stage-1 of the ECMAScript. Hope that this proposal will be finalized soon.

Observer pattern is a one-to-many dependency between objects. There is one independent variable (called publisher), while there are many dependent variables (called subscribers). Once publisher changes state, all its dependent subscribers are notified and update automatically

class Observer{
	publishers: Publisher[] = []
	subscribe(publisher: Publisher){
    	if (!this.publishers.includes(publisher)) {
        	this.publishers = [...this.publishers, publisher]
        	//also return an unsubscription
        	return () => {this.publishers = this.publishers.map(p => p !== publisher)}
    	for (const publisher of this.publishers) publisher.notify()
class Publisher {

22. State (another name: Objects for States)

An object has its own internal state and its behavior of a same method changes based on the state at the time of being called.

class Connection {
	state: 'open' | 'connecting' | 'connected' | 'closed' = 'open'
    open(){} // throw error if this.state is not 'open'
    close(){} // throw error if this.state is 'closed'
    send(data: string){} // throw error if this.state is not 'connected'

23. Strategy (another name: Policy)

When there is a family of algorithms. Strategy pattern lets the algorithm vary independently from clients that use it, encapsulates each algorithm and make them interchangeable.

Passportjs is a well-known nodejs library which implements this pattern.

class Connection {
	constructor(public strategy: Strategy){}
    send(data: string){this.strategy.send(data)}
class Strategy {
	send(){throw new Error('not implemented')}
class TCPStrategy extends Strategy {
class UDPStrategy extends Strategy {

24. Visitor

In your application, there are multiple types of element, and also multiple types of operations that each operation has its own different way to treat each element type.

Without visitor pattern, each concrete element class has to check the operation type being applied to behave. This increases maintenance burden when new operation is defined. Because all concrete element implementation has to modify its behavior on the new operation.

Visitor pattern make an abstract for all operations via a Visitor class whose concrete sub-class must define it own action for each element type. All elements delegate to an Element class which simply calls accept(visitor)  when being visited.

This pattern should be applied when

  • the Element class has multiple sub-classes and you want to perform operations on these elements that depend on concrete sub-class.
  • operations are distinct and unrelated. Visitor lets you keep related operations together by defining them in one class
  • Element class is rarely changed or added new sub-class. Because whenever a new sub-class is defined, all concrete implementations (sub-classes) Visitor have to implement behavior on the newly defined element. A practice solution in this case is defining default behavior for unknown element. However, it is not recommended because it is prone to forget adding the implementation on new element in some Visitor 's sub-classes.
class Visitor{
	visitA(a: ElementA){throw new Error('n_iplm')}
	visitB(b: ElementB){throw new Error('n_iplm')}
class Element{
	accept(visitor: Visitor){throw new Error('not implemented')}
class ElementA extends Element {
	accept(visitor: Visitor){visitor.visitA(this)}
class ElementB extends Element {
	accept(visitor: Visitor){visitor.visitB(this)}

After the Design Patterns Elements of Reusable Object-Oriented Software book had been published, there were many new design patterns have been invented. There is a list of various patterns from wikipedia page

Software design patterns list

25. Dependency injection

Injector decides which Service to be used in Client.
Without dependency injection, client initiates service in its constructor.

Angular is a well-known framework which make use of this pattern.

//main application is an injector
const service = new MyService()
const client = new Client(service)

26. Lazy initialization

Similar to fly weight. Delay the creation of an object or calculation of a value or some expensive process until the first time it is needed.

let googleMapsInstancePromise: Promise<GoogleMaps>
const getGoogleMapsInstance = () => googleMapsInstancePromise || (googleMapsInstancePromise = (async () => {
	const GoogleMaps = await import('./GoogleMaps')
    return new GoogleMaps()

Also check my related post Promise based semaphore pattern in Javascript.

27. Multiton

Generalization of singleton pattern, so-called registry of singleton


Demerit: be careful of memory leak because the initialized instances stays for the whole application life cycle.

28. Object pool

To increase performance, some cases you might want to pay the cost of memory by storing initialized instances stead of creating/destroying them by demand.

const pool: {[key: number]: number} = {}
const fib = (n: number) => pool[n] !== undefined
	? pool[n]
    : (pool[n] = calculateFib(n))

29. Resource acquisition is initialization (RAII)

Used to describe a particular language behavior.

Resource must be acquired during constructor and released in destructor.

class Session {
	file: File
	constructor(path: string){this.file = new File(path)}

30. Front controller

Is a popular pattern used in web application (server) to handle all requests/navigation in a single controller.

Front controller is a specialized pattern of mediator.
Front controller is a specialized pattern of controller in MVC pattern.

The opposite pattern of front controller is page controller.
In front controller pattern, there is only front controller handling all requests. In opposite, in page controller pattern, there are many page controllers in which each page controller handles a specific request.

It is often to see apache config to forward all requests to index.php or front-controller.php via .htaccess config

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?path=$1 [NC,L,QSA]

Nodejs example

import express from 'express'
const app = express()

app.get('/login', loginMiddleware)
app.get('/favicon.ico', (req, res) => res.sendFile(path.resolve(__dirname, 'assets/favicon.ico')))

31. Marker

An interface to add meta information to an object in run-type.

There are many real-life cases where this pattern is applied.

In apollo graphql __typename key is added for an object to be type-distinguished from others, helping typescript to correctly interpret object's type.

In Java, Serializable interface indicates class whose instances can be written to ObjectOutputStream.

class Transportation {
	speed: number
class Car extends Transporation {
	__typename = 'Car'
class Bike extends Transporation {
	__typename = 'Bike'

32. Module

In module pattern, source code is separated by functionality to manage source code better.
In programming language which does not support module natively, developers can design their code to follow this design pattern.

export const a = 1

import {a} from './a'

33. Twin

Simulate multiple inheritance in programming languages that do not support multiple inheritance natively.

Method 1: use multiple fields

class Parent1 {}
class Child1 extends Parent1 {
	//overwrite Parent1's fields
class Parent2 {}
class Child2 extends Parent2 {
	//overwrite Parent2's fields
class Parent3 {}
class Child3 extends Parent3 {
	//overwrite Parent3's fields
class CommonChild { //multiple inheritance
	child1: Child1
    child2: Child2
    child3: Child3
    //other fields

Method 2: pivot one, and twin others

class Parent1 {}
class Parent2 {}
class Child2 extends Parent2 {
	//overwrite Parent2's fields
class CommonChild extends Parent1 { //multiple inheritance
	twin: Child2
    //other fields

34. Blackboard

Firstly introduced and applied in speech recognition application by members of the HEARSAY-II project.

This design includes three components

  • blackboard: store information, results from many algorithms which run separately in knowledge component
  • knowledge: a set of programs/algorithms which runs and post their result to the blackboard
  • control: use result in the blackboard for further processing, or check status and give final output of the whole system

For example, in a speech recognition application: a tape of audio recording is cut to many small pieces. Each piece of the audio recording is recognized by a process called knowledge.
This knowledge process posts its output/result to the blackboard in appropriate position.
The control component periodically checks the blackboard for semantics of group of words (sentence). If the sentence should be improved, the control lets the knowledge process run again to provide next prediction. Or the control can fix the sentence and give final result.

class BlackBoard {
	update(result: number, pos: number){}
class Knowledge {
	constructor(private board: BlackBoard, private pos: number)
    run(data: Int8Array){
    	this.board.update(this.dataToResult(data), this.pos)
    dataToResult(data: Int8Array){}
class Control {
	intervalId: number
	constructor(private board: BlackBoard){}
    	if (!this.intervalId) this.stop()
    	this.intervalId = setInterval(() => this.check(), 3000)
    	if (this.intervalId) clearInterval(this.intervalId)
        this.intervalId = undefined

35. Null object

Instead of returning a null reference, return a null object which implements default behavior. This reduces complication of the caller in a way that the caller does not have to care about nullity of the returned result from callee.

class Animal {countLegs{throw new Error('n_impl')}}
class Human extends Animal {
	countLegs(){return 2}
class Dog extends Animal {
	countLegs(){return 4}
class NullAnimal extends Animal { // null object class
	countLegs(){return 0}

const createAnimal = (type: string){
	return type === 'human' ? new Human() : type === 'dog' ? new Dog() : new NullAnimal()

36. Servant

Respecting the rule "Separation of Concerns", servant class in server pattern is a class which provides some behavior to a group of classes. Let's see example

interface IHasTime {
	setTime(time: number): void
    getTime(): number
class TimeHandler {//servant class of IHasTime
	constructor(private timer: IHasTime){}
    nextDay()//move by 1 day next
//following classes are redudant to explain the servant class definition. They are defined for completeness sake
class Clock extends IHasTime {
	//implement time in a day
class Calendar extends IHasTime {
	//implement date only
class Timestamp extends IHasTime {
	//implement date and time

Another well-known example is when we want some additional features on default class, e.g. Date, String, .... It should never modify/add method to these classes directly. Instead, we can define servant classes for our own purpose without affecting the global environment.

37. Specification

Encapsulate domain rules in objects. In a nutshell, this pattern is no more than the combinations of multiple predicates. The specification can be chained with other specifications, making a new specification easily maintainable and highly customizable.

//generic specification definition
class Predicate<T> { //or Specification. I prefer the 'Predicate' name
	constructor(private predicate: (u: T) => boolean){}
	and(other: Predicate){ return new AndPredicate(this, other)}
    or(other: Predicate){return new OrPredicate(this, other)}
    not(){return new NotPredicate(this)}
    isSatisifiedBy(obj: T){return this.predicate(u)}
class AndPredicate<T> extends Predicate<T> {
	constructor(private p1: Predicate<T>, private p2: Predicate<T>){}
    isSatisifiedBy(obj: T){return this.p1.isSatisifiedBy(obj) && this.p2.isSatisifiedBy(obj)}
class OrPredicate<T> extends Predicate<T>{/*similar to AndPredicate*/}
class NotPredicate<T> extends Predicate<T>{
	constructor(private predicate: Predicate<T>){}
    isSatisifiedBy(obj: T){return !this.predicate.isSatisifiedBy(obj)}

//application of specification pattern
enum Role {
class User {
	public roles: Role[] //user can have multiple roles
const userHasEntranceKey = new Predicate((u: User) => /*check user role to have privilege to keep entrance key*/)
class CanDiscount extends Predicate<User> {
	constructor(private order: Order){}
	isSatisifiedBy(obj: User){
    	//more complicated logic to check if an order can be discount by the input user
        //e.g. this.order.total > 1000

userHasEntranceKey.and(new CanDiscount(order)).isSatisifiedBy(user)

38. Closure

Closure is a function with an associated environment.

const addX = x => y => x + y
const add3 = addX(3)
console.log(add3(1)) //print 4

Another more complicated and real-life example

import {AnyObject} from 'final-form'
import React, {ComponentType} from 'react'
import {Field, FieldRenderProps} from 'react-final-form'

export type IFieldsRenderProps<
  FieldTypes extends {[key: string]: [any, HTMLElement]}
  > = {
  [key in keyof FieldTypes]: FieldRenderProps<FieldTypes[key][0], FieldTypes[key][1]>

const Fields = <
  FieldTypes extends {[key: string]: [AnyObject, HTMLElement]},
  TProps extends {},
    names, component: Comp, props = {} as TProps
  }: {names: ReadonlyArray<keyof FieldTypes & string>, props?: Omit<TProps, keyof FieldTypes>}
    & {component: ComponentType<TProps & IFieldsRenderProps<FieldTypes>>}
) => names.reduce((acc, cur) => p => <Field name={cur}>
    {renderProps => acc({...p, [cur]: renderProps})}
  (p: IFieldsRenderProps<FieldTypes>) => <Comp {...props} {...p}/>
)({} as unknown as IFieldsRenderProps<FieldTypes>)

Fields.displayName = 'Fields'
export default Fields

Without type definition the part which makes use of closure will be

const Fields = ({
    names, component: Comp, props = {} as TProps
}) => names.reduce((acc, cur) => p => <Field name={cur}>
    {renderProps => acc({...p, [cur]: renderProps})}
  p => <Comp {...props} {...p}/>

The reducer parameter (first parameter of .reduce) returns a closure, which helps aggregating value in array from end to start.

One merit of using closure is that it support type inference better than procedure programming.

39. Currying

...to be continued...

This post is getting much longer than expected.
Functional programming is one of my favorite topic. However, I have run out of my time for this post.
Let me leave some references here for who are interested can refer for further detail. I will go back to continue this post when I am sparse.






What is call/cc?
I’ve tried several times to grasp the concept of continuations and call/cc. Every single attempt was a failure. Can somebody please explain me these concepts, ideally with more realistic examples t...





What is the difference between currying and partial application?
I quite often see on the Internet various complaints that other peoples examples of currying are not currying, but are actually just partial application. I’ve not found a decent explanation of what



I just don’t get continuations!
What are they and what are they good for? I do not have a CS degree and my background is VB6 -&gt; ASP -&gt; ASP.NET/C#. Can anyone explain it in a clear and concise manner?
Are functions in JavaScript tail-call optimized?
I have been trying to understand Tail call optimization in context of JavaScript and have written the below recursive and tail-recursive methods for factorial(). Recursive: function factorial (n)...