Skip to content

Chain of Responsibility Design Pattern

Video Lecture

Section Video Links
Chain of Responsibility Pattern Chain of Responsibility Chain of Responsibility Pattern 
Chain of Responsibility Use Case Chain of Responsibility Use Case Chain of Responsibility Use Case 

Overview

...Refer to Book or Videos for extra content.

Terminology

...Refer to Book or Videos for extra content.

Chain of Responsibility UML Diagram

Chain of Responsibility Design Pattern

Source Code

...Refer to Book or Videos for extra content.

./src/chain-of-responsibility/chain-of-responsibility-concept.ts

// The Chain Of Responsibility Pattern Concept

interface IHandler {
    // The Handler Interface that the Successors should implement
    handle(payload: number): number
}

class Successor1 implements IHandler {
    // A Concrete Handler
    handle(payload: number) {
        console.log(`Successor1 payload = ${payload}`)
        const test = Math.floor(Math.random() * 2) + 1
        if (test === 1) {
            payload += 1
            payload = new Successor1().handle(payload)
        } else {
            payload -= 1
            payload = new Successor2().handle(payload)
        }
        return payload
    }
}

class Successor2 implements IHandler {
    // A Concrete Handler
    handle(payload: number) {
        console.log(`Successor2 payload = ${payload}`)
        const test = Math.floor(Math.random() * 3) + 1
        if (test === 1) {
            payload = payload * 2
            payload = new Successor1().handle(payload)
        } else if (test === 2) {
            payload = payload / 2
            payload = new Successor2().handle(payload)
        } // if test = 3 then assign no further successors
        return payload
    }
}

class Chain {
    // A chain with a default first successor
    start(payload: number) {
        // Setting the first successor that will modify the payload
        return new Successor1().handle(payload)
    }
}

// The Client
const CHAIN = new Chain()
const PAYLOAD = 1
const OUT = CHAIN.start(PAYLOAD)
console.log(`Finished result = ${OUT}`)

Output

node ./dist/chain-of-responsibility/chain-of-responsibility-concept.js
Successor1 payload = 1
Successor2 payload = -1
Successor2 payload = -0.5
Successor2 payload = -0.25
Successor1 payload = -0.5
Successor1 payload = 0.5
Successor2 payload = -1.5
Finished result = -1.5

Chain of Responsibility Use Case

...Refer to Book or Videos for extra content.

Example UML Diagram

Chain of Responsibility Design Pattern

Source Code

./src/chain-of-responsibility/client.ts

// An ATM Dispenser that dispenses denominations of notes

import ATMDispenserChain from './atm-dispenser-chain'

const ATM = new ATMDispenserChain()
console.log('Enter amount to withdrawal : ')
process.stdin.on('data', (data: string) => {
    if (parseInt(data)) {
        const amount = parseInt(data)
        if (amount < 10 || amount % 10 != 0) {
            console.log(
                'Amount should be positive and in multiple of 10s.'
            )
        } else {
            // process the request
            ATM.chain1.handle(amount)
            console.log('Now go spoil yourself')
            process.exit()
        }
    } else {
        console.log('Please enter a number.')
    }
})

./src/chain-of-responsibility/atm-dispenser-chain.ts

// The ATM Dispenser Chain

import Dispenser10 from './dispenser10'
import Dispenser20 from './dispenser20'
import Dispenser50 from './dispenser50'

export default class ATMDispenserChain {
    chain1: Dispenser50
    chain2: Dispenser20
    chain3: Dispenser10

    constructor() {
        // initializing the successors chain
        this.chain1 = new Dispenser50()
        this.chain2 = new Dispenser20()
        this.chain3 = new Dispenser10()
        // Setting a default successor chain that will process the 50s first,
        // the 20s second and the 10s last.The successor chain will be
        // recalculated dynamically at runtime.
        this.chain1.nextSuccessor(this.chain2)
        this.chain2.nextSuccessor(this.chain3)
    }
}

./src/chain-of-responsibility/idispenser.ts

interface IDispenser {
    nextSuccessor(successor: IDispenser): void
    handle(amount: number): void
}

./src/chain-of-responsibility/dispenser10.ts

// A dispenser of £10 notes

export default class Dispenser10 implements IDispenser {
    // Dispenses £10s if applicable, otherwise continues to next successor
    #successor: IDispenser | undefined

    nextSuccessor(successor: IDispenser): void {
        // Set the next successor
        this.#successor = successor
    }

    handle(amount: number): void {
        // Handle the dispensing of notes"
        if (amount >= 10) {
            const num = Math.floor(amount / 10)
            const remainder = amount % 10
            console.log(`Dispensing ${num} £10 note`)
            if (remainder !== 0) {
                ;(this.#successor as IDispenser).handle(remainder)
            }
        } else {
            ;(this.#successor as IDispenser).handle(amount)
        }
    }
}

./src/chain-of-responsibility/dispenser20.ts

// A dispenser of £20 notes

export default class Dispenser20 implements IDispenser {
    // Dispenses £10s if applicable, otherwise continues to next successor
    #successor: IDispenser | undefined

    nextSuccessor(successor: IDispenser): void {
        // Set the next successor
        this.#successor = successor
    }

    handle(amount: number): void {
        // Handle the dispensing of notes"
        if (amount >= 20) {
            const num = Math.floor(amount / 20)
            const remainder = amount % 20
            console.log(`Dispensing ${num} £20 note`)
            if (remainder !== 0) {
                ;(this.#successor as IDispenser).handle(remainder)
            }
        } else {
            ;(this.#successor as IDispenser).handle(amount)
        }
    }
}

./src/chain-of-responsibility/dispenser50.ts

// A dispenser of £50 notes

export default class Dispenser50 implements IDispenser {
    // Dispenses £10s if applicable, otherwise continues to next successor
    #successor: IDispenser | undefined

    nextSuccessor(successor: IDispenser): void {
        // Set the next successor
        this.#successor = successor
    }

    handle(amount: number): void {
        // Handle the dispensing of notes"
        if (amount >= 50) {
            const num = Math.floor(amount / 50)
            const remainder = amount % 50
            console.log(`Dispensing ${num} £50 note`)
            if (remainder !== 0) {
                ;(this.#successor as IDispenser).handle(remainder)
            }
        } else {
            ;(this.#successor as IDispenser).handle(amount)
        }
    }
}

Output

node ./dist/chain-of-responsibility/client.js
Enter amount to withdrawal :
180
Dispensing 3 £50 note
Dispensing 1 £20 note
Dispensing 1 £10 note
Now go spoil yourself

Summary

...Refer to Book or Videos for extra content.