装饰器模式、适配器模式

装饰器模式、适配器模式

1.Decorator 装饰器模式

基本原理

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口

  • Component Interface:对象的接口(共可装饰类和装饰类使用)
  • Component:可装饰的对象
  • Decorator:装饰器类,将额外任务应用于组件的类,它还实现了相同的组件接口

在这里插入图片描述

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()})`
    }
}

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

// Component类被Decorator类装饰
const Decorated = new Decorator(COMPONENT)
console.log(Decorated.method()) //Decorator Method(Component Method)

//被装饰过的类Component可以在被装饰(套娃咯)
const Decorated2 = new Decorator(Decorated)
console.log(Decorated2.method()) //Decorator Method(Decorator Method(Component Method))

应用举例

让我们创建一个名为Value类来保存一个数组,然后添加对数字(Value)进行加法 (Add)和减法 (Sub) 的装饰器来实现给对象添加新的功能,而不改变对象本身的。

AddSub 装饰器可以直接接受数字、自定义 Value 对象或其他 AddSub 装饰器

在这里插入图片描述

interface IValue {
    value: number
}

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
    }
}


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
    }
}


//装饰器类
class Value implements IValue {
    value: number
    constructor(value: number) {
        this.value = value
    }
}

//装饰器
const A = new Value(1) 
const B = new Value(2)
const C = new Value(5)

console.log(new Add(A, B).value) //3
console.log(new Add(A, 100).value) //101
console.log(new Sub(C, A).value) //4
console.log(new Sub(new Add(C, B), A).value) //6
console.log(new Sub(100, 101).value) //-1
console.log(new Add(new Sub(new Add(C, B), A), 100).value)//106
console.log(new Sub(123, Add(C, C)).value)//113
console.log(new Add(new Sub(new Add(C, 10), A), 100).value) //114
console.log(A.value) //1
console.log(B.value) //2
console.log(C.value) //5

2.Adapter适配器模式

一个比较常用构造型设计模式——Adapter适配器模式,关于适配器模式,我们主要需要学习它的两种实现方式,类适配器对象适配器,以及 5 种常见的应用场景

基本原理

适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。对于这个模式,有一个经常被拿来解释它的例子,就是 USB 转接头充当适 配器,把两种不兼容的接口,通过转接变得可以一起工作。

比如:你可能有两个相似的类,但它们具有不同的方法签名,因此你在其中一个方法签名之上创建一个适配器,以便更容易在调用时中实现和扩展

适配器模式与前面的装饰器模式有些类似,都是创建完对象后使用,不过设配器模式不能像装饰器模式一样递归调用

在这里插入图片描述

  • Target: 需要适配的目标接口/类
  • Adapter:适配器类
  • Adapter Interface: 适配器需要实现的接口,以使目标与客户端兼容(插口和线同)
  • Client:调用适配器的应用程序
// 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中没有methodA,所以就需要创建适配器来兼容
    #classB: ClassB

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

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

const ITEMS = [new ClassA(), new ClassB()]
ITEMS.forEach((item) => {
    if (item instanceof ClassB) {
        item.methodB()
    } else {
        item.methodA()
    }
})

const ADAPTED = [new ClassA(), new ClassBAdapter()]
ADAPTED.forEach((item) => {
    item.methodA()
})

method A
method B
method A
method B

就只是在外部包了一层,因此除了上面的对象适配器还有一种称为类适配器也是同样的效果,类适配器则是采用继承来实现,将将原method继承到复合Target接口的method上,比上面的示例还简单,这里就不写出示例了。

而具体选用那种需要根据实际情况选择

应用举例

假设有一个使用不同工具生产立方体的情形,而不同的工具是由不同的公司发明的,应用程序通过UI界面输入width、height、depth来生产立方体(接口),而输入的这些参数只与A公司的生产工具所需参数相同,而B公司的生产工具的所需参数与A公司并不同(接口也就不同了),那么为了连个接口适配,将B公司的生产工具适配A公司生产工具的接口。

这样在有一个公司(供应商)在忙时就可以使用另一个供应商了

在这里插入图片描述

interface ICubeA {
    manufacture(width: number, height: number, depth: number): boolean
}

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

    manufacture(width: number, height: number, depth: number): boolean {
        //如果是忙碌中则制作一个正方体
        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 //忙碌中
    }
}

//一个假想的B公司的立方体制作工具
interface ICubeB {
    create(
        top_left_front: [number, number, number],
        bottom_right_back: [number, number, number]
    ): boolean
}
class CubeB implements ICubeB {
    static last_time = Date.now()

    create(
        top_left_front: [number, number, number],
        bottom_right_back: [number, number, number]
    ): boolean {
        // 如果不忙,那就用坐标制造一个立方体
        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
        }
    }
}
//适配器类,使CubeB类也能使用ICubeA接口
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
    }
}
const totalCubes = 5
let counter = 0

const manufactureCube = () => {
    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)
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

seeooco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值