Skip to content

Adapter Design Pattern

Video Lecture

Section Video Links
Adapter Pattern Adapter Adapter Pattern 
Adapter Use Case Adapter Use Case Adapter Use Case 

Overview

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

Terminology

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

Adapter UML Diagram

Adapter Pattern UML Diagram

Source Code

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

./src/adapter/adapter-concept.ts

// Adapter Concept Sample Code

interface IA {
    methodA(): void
}

class ClassA implements IA {
    methodA() {
        console.log('method A')
    }
}

interface IB {
    methodB(): void
}

class ClassB implements IB {
    methodB() {
        console.log('method B')
    }
}

class ClassBAdapter implements IA {
    // ClassB does not have a methodA, so we can create an adapter

    #classB: ClassB

    constructor() {
        this.#classB = new ClassB()
    }

    methodA() {
        'calls the class b method_b instead'
        this.#classB.methodB()
    }
}

// The Client
// Before the adapter I need to test the objects class to know which
// method to call.
const ITEMS = [new ClassA(), new ClassB()]
ITEMS.forEach((item) => {
    if (item instanceof ClassB) {
        item.methodB()
    } else {
        item.methodA()
    }
})

// After creating an adapter for ClassB I can reuse the same method
// signature as ClassA (preferred)
const ADAPTED = [new ClassA(), new ClassBAdapter()]
ADAPTED.forEach((item) => {
    item.methodA()
})

Output

node ./dist/adapter/adapter-concept.js
method A
method B
method A
method B

Adapter Use Case

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

Example UML Diagram

Adapter Pattern in Context

Source Code

./src/adapter/client.ts

// Adapter Example Use Case
import CubeA from './cube-a'
import CubeBAdapter from './cube-b-adapter'

const totalCubes = 5
let counter = 0

const manufactureCube = () => {
    // produce 5 cubes from which ever supplier can manufacture it first
    const width = Math.floor(Math.random() * 10) + 1
    const height = Math.floor(Math.random() * 10) + 1
    const depth = Math.floor(Math.random() * 10) + 1
    let cube = new CubeA()
    let success = cube.manufacture(width, height, depth)
    if (success) {
        counter = counter + 1
    } else {
        // try other manufacturer
        console.log('Company A was busy, so trying company B')
        cube = new CubeBAdapter()
        success = cube.manufacture(width, height, depth)
        if (success) {
            counter = counter + 1
        } else {
            console.log('Company B was busy, so trying company A')
        }
    }
}

// wait some time between manufacturing each cube
const interval = setInterval(() => {
    manufactureCube()
    if (counter >= totalCubes) {
        clearInterval(interval)
        console.log(`${totalCubes} cubes have been manufactured`)
    }
}, 1000)

./src/adapter/cube-a.ts

// A hypothetical Cube tool from Company A
export interface ICubeA {
    manufacture(width: number, height: number, depth: number): boolean
}

export default class CubeA implements ICubeA {
    static last_time = Date.now()

    manufacture(width: number, height: number, depth: number): boolean {
        // if not busy, then manufacture a cube with dimensions
        const now = Date.now()
        if (now > CubeA.last_time + 1500) {
            console.log(
                `Company A built Cube with dimensions ${width}x${height}x${depth}`
            )
            CubeA.last_time = now
            return true
        }
        return false // busy
    }
}

./src/adapter/cube-b.ts

// A hypothetical Cube tool from Company B
export interface ICubeB {
    create(
        top_left_front: [number, number, number],
        bottom_right_back: [number, number, number]
    ): boolean
}

export default class CubeB implements ICubeB {
    static last_time = Date.now()

    create(
        top_left_front: [number, number, number],
        bottom_right_back: [number, number, number]
    ): boolean {
        // if not busy, then manufacture a cube with coords
        const now = Date.now()
        if (now > CubeB.last_time + 3000) {
            console.log(
                `Company B built Cube with coords [${top_left_front[0]},${top_left_front[1]},${top_left_front[2]}],[${bottom_right_back[0]},${bottom_right_back[1]},${bottom_right_back[2]}]`
            )
            CubeB.last_time = now
            return true
        } else {
            return false // busy
        }
    }
}

./src/adapter/cube-b-adapter.ts

// Adapter for CubeB that implements ICubeA
import { ICubeA } from './cube-a'
import CubeB from './cube-b'

export default class CubeBAdapter implements ICubeA {
    #cube: CubeB

    constructor() {
        this.#cube = new CubeB()
    }

    manufacture(width: number, height: number, depth: number): boolean {
        const success = this.#cube.create(
            [0 - width / 2, 0 - height / 2, 0 - depth / 2],
            [0 + width / 2, 0 + height / 2, 0 + depth / 2]
        )
        return success
    }
}

Output

node ./dist/adapter/client.js
Company A was busy, so trying company B
Company B was busy, so trying company A
Company A built Cube with dimensions 6x5x10
Company A was busy, so trying company B
Company B built Cube with coords [-4,-3,-2.5],[4,3,2.5]
Company A built Cube with dimensions 4x5x3
Company A was busy, so trying company B
Company B was busy, so trying company A
Company A built Cube with dimensions 10x2x1
Company A was busy, so trying company B
Company B built Cube with coords [-0.5,-2,-2.5],[0.5,2,2.5]
5 cubes have been manufactured

Summary

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