Skip to content

Decorator Design Pattern

Video Lecture

Section Video Links
Decorator Pattern Decorator Decorator Pattern 
Decorator Use Case Decorator Use Case Decorator Use Case 

Overview

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

Terminology

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

Decorator UML Diagram

Decorator Pattern UML Diagram

Source Code

./src/decorator/decorator-concept.ts

// Decorator Concept Sample Code

interface IComponent {
    method(): string
}

class Component implements IComponent {
    method(): string {
        return 'Component Method'
    }
}

class Decorator implements IComponent {
    #object: IComponent

    constructor(object: IComponent) {
        this.#object = object
    }

    method(): string {
        return `Decorator Method(${this.#object.method()})`
    }
}

// The Client
const COMPONENT = new Component()
console.log(COMPONENT.method())

// The component can be decorated
const Decorated = new Decorator(COMPONENT)
console.log(Decorated.method())

// The decorated component can be decorated again
const Decorated2 = new Decorator(Decorated)
console.log(Decorated2.method())

Output

node ./dist/decorator/decorator-concept.js
Component Method
Decorator Method(Component Method)
Decorator Method(Decorator Method(Component Method))

Decorator Use Case

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

Example UML Diagram

Decorator Pattern in Context

Source Code

./src/decorator/client.ts

// Decorator Use Case Example Code

import Value from './value'
import Add from './add'
import Sub from './sub'

const A = Value(1)
const B = Value(2)
const C = Value(5)

console.log(Add(A, B).value)
console.log(Add(A, 100).value)
console.log(Sub(C, A).value)
console.log(Sub(Add(C, B), A).value)
console.log(Sub(100, 101).value)
console.log(Add(Sub(Add(C, B), A), 100).value)
console.log(Sub(123, Add(C, C)).value)
console.log(Add(Sub(Add(C, 10), A), 100).value)
console.log(A.value)
console.log(B.value)
console.log(C.value)

./src/decorator/value.ts

export interface IValue {
    value: number
}

class _Value implements IValue {
    value: number
    constructor(value: number) {
        this.value = value
    }
}

export default function Value(value: number): IValue {
    return new _Value(value)
}

./src/decorator/add.ts

import { IValue } from './value'

class _Add implements IValue {
    value: number
    constructor(val1: IValue | number, val2: IValue | number) {
        const left = Object.prototype.hasOwnProperty.call(val1, 'value')
            ? (val1 as IValue).value
            : (val1 as number)
        const right = Object.prototype.hasOwnProperty.call(val2, 'value')
            ? (val2 as IValue).value
            : (val2 as number)
        this.value = left + right
    }
}

export default function Add(
    val1: IValue | number,
    val2: IValue | number
): IValue {
    return new _Add(val1, val2)
}

./src/decorator/sub.ts

import { IValue } from './value'

class _Sub implements IValue {
    value: number
    constructor(val1: IValue | number, val2: IValue | number) {
        const left = Object.prototype.hasOwnProperty.call(val1, 'value')
            ? (val1 as IValue).value
            : (val1 as number)
        const right = Object.prototype.hasOwnProperty.call(val2, 'value')
            ? (val2 as IValue).value
            : (val2 as number)
        this.value = left - right
    }
}

export default function Sub(
    val1: IValue | number,
    val2: IValue | number
): IValue {
    return new _Sub(val1, val2)
}

Output

node ./dist/decorator/client.js
3
101
4
6
-1
106
113
114
1
2
5

Summary

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