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

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

With or without you 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 a bunch of blog posts, books that 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.

In 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 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 a class based on an 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 the old code base that 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 that can interpret a language and stores the input string in an internal state instead of the raw input.
Consider regular expression as a language, then RegExp is an interpreter.

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

4. Template method

Superclass (parent class) defines the skeleton of an operation which, operation, includes one or more sub-operations. These sub-operations can be defined/re-defined by sub-classes. The skeleton keeps the same regardless of 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 that 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 object-keeping behaviors (methods) as a prototypical instance. Every time a new object is created, the new object copies behaviors from the prototypical instance. This prototype pattern is supported native in Javascript and it brings much strength to the Javascript language itself. Object.create method takes the first parameter as the prototype and instantiates a 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 enables the implementation can be changed at runtime.
This pattern is similar but different from the 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 the 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 it difficult to extend the IPet interface, or add a new implementation.

11. Composite

A composite pattern is used to represent part-whole hierarchies.

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

The composite pattern helps to treat 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 that allows the individual objects to directly implementing the interface while the compositions forward 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 an 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 users without knowledge about the sub-system's internal process.

One of the best examples is jQuery. It wraps the DOM element and provides a more user-friendly API interface and handles 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 catch memory overflow. Use it at your own risk.

15. Proxy (another name: Surrogate)

As the name suggested, a proxy is a placeholder to control other objects. 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 objects in OS. The file object in OS may be another proxy which exposes an interface to connect user call and real data stored in the hard disk.

16. Chain of Responsibility

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

The syntax looks similar to the decorator, but they serve different purposes. The 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, the object which handles how to execute the command.
This pattern is also able to store a 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 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
  • The mediator is more like a controller in an 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 wants to restore the originator's state later.

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

Memento: like a snapshot of the 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)

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

The 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 the 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 the 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. The strategy pattern lets the algorithm vary independently from clients that use it, encapsulates each algorithm, and makes them interchangeable.

Passportjs is a well-known nodejs library that 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 elements, 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 the maintenance burden when a new operation is defined. Because all concrete element implementation has to modify its behavior on the new operation.

Visitor pattern makes an abstract for all operations via a Visitor class whose concrete sub-class must define its own action for each element type. All elements delegate to a 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 subclass 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 elements. However, it is not recommended because it is prone to forget to add the implementation of the 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 that makes use of this pattern.

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

26. Lazy initialization

Similar to flyweight. 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 stay for the whole application life cycle.

28. Object pool

To increase performance, in 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.

The resource must be acquired during the constructor and released in the destructor.

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

30. Front controller

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

The front controller is a specialized pattern of a mediator.
The front controller is a specialized pattern of a controller in the MVC pattern.

The opposite pattern of the front controller is the page controller.
In the front controller pattern, there is only a front controller handling all requests. In opposite, in the 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 the 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 the 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 inheritances in programming languages that do not support multiple inheritances 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 results in the blackboard for further processing, or check the status and give the final output of the whole system

For example, in a speech recognition application: a tape of audio recording is cut into 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 an appropriate position.
The control component periodically checks the blackboard for the semantics of a group of words (sentence). If the sentence should be improved, the control lets the knowledge process run again to provide the next prediction. Or the control can fix the sentence and give the 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 the complication of the caller in a way that the caller does not have to care about the nullity of the returned result from the 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 that provides some behavior to a group of classes. Let's see an 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 the default class, e.g. Date, String, .... It should never modify/add methods 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

The 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 aggregate value in an array from end to start.

One merit of using closure is that it supports 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 topics. However, I have run out of time for this post.
Let me leave some references here for those 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)...
Buy Me A Coffee