【09】装饰器模式

装饰器模式是一种结构型设计模式,用于向现有对象添加新功能而不改变其结构。文章介绍了装饰器模式的概念、JavaScript和TypeScript中的实现,以及在ES、react-redux和Angular中的应用。此外,还探讨了AOP(面向切面编程)的概念,并给出了一段装饰器实现的日志记录功能的示例。
摘要由CSDN通过智能技术生成

01-介绍

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

例如,手机上套一个壳可以保护手机,壳上粘一个指环,可以挂在手指上不容易滑落,这就是一种装饰。手机还是那个手机,手机的功能一点都没变,只是在手机的外面装饰了一些其他附加的功能。日常生活中,这样的例子非常多。

在这里插入图片描述

function decorate(phone) {
    phone.fn3 = function () {
        console.log('指环')
    }
}
const phone = {
    name: 'iphone12',
    fn1() {}
    fn2() {}
}
const newPhone = decorate(phone)

而 ES 语法允许我们这样写(其实就是语法糖),后面会详细讲

// 伪代码,不能运行
@decorate
const phone = { ... }

02-演示

在这里插入图片描述

class Circle {
    draw() {
        console.log('画一个圆')
    }
}

class Decorator {
    private circle: Circle
    constructor(circle: Circle) {
        this.circle = circle
    }
    draw() {
        this.circle.draw()
        this.setBorder()
    }
    private setBorder() {
        console.log('设置边框颜色')
    }
}

const circle = new Circle()
circle.draw()

const decorator = new Decorator(circle)
decorator.draw()

是否符合设计原则?

5 大设计原则中,最重要的就是:开放封闭原则,对扩展开放,对修改封闭

  • 装饰器和目标分离,解耦
  • 装饰器可自行扩展
  • 目标也可自行扩展

03-场景

ES 引入了 Decorator 语法,TS 也支持

PS:在 tsconfig.json 中加 experimentalDecorators: true

装饰 class

// 装饰器
function testable(target: any) {
    target.isTestable = true
}

@testable
class Foo {
    static isTestable?: boolean
}

console.log(Foo.isTestable) // true

可以传入参数

// 装饰器工厂函数
function testable(val: boolean) {
    // 装饰器
    return function (target: any) {
        target.isTestable = val
    }
}

@testable(false)
class Foo {
    static isTestable?: boolean
}

console.log(Foo.isTestable) // false

装饰 class 方法

function readOnly(target: any, key: string, descriptor: PropertyDescriptor) {
    // console.log('target', target)
    // console.log('key', key)
    descriptor.writable = false
}

function configurable(val: boolean) {
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = val
    }
}

class Foo {
    private _name = '张三'
    private _age  = 20

    @readOnly
    getName() {
        return this._name
    }

    @configurable(false)
    getAge() {
        return this._age
    }
}

const f = new Foo()
// f.getName = () => { return 'hello' } // 会报错
console.log(f.getName())

// @ts-ignore
// console.log( Object.getOwnPropertyDescriptor(f.__proto__, 'getAge') ) // { configurable: false }
console.log(f.getAge)

PS:其实 TS 本身有 readOnly 语法,但这里就是一个演示。

react-redux

react-redux 的基本使用如下。文档参考 https://www.redux.org.cn/docs/basics/UsageWithReact.html

import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

如果使用装饰器就是

import { connect } from 'react-redux'

// 装饰器
@connect(mapStateToProps, mapDispatchToProps)
export default VisibleTodoList extends React.Component { }

Angular 定义组件

文档 https://angular.io/start

import { Component, OnInit } from '@angular/core';

// 装饰器,定义 class 为组件
@Component({
  selector: 'app-product-alerts',
  templateUrl: './product-alerts.component.html',
  styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent implements OnInit {
  constructor() { }
  ngOnInit() {}
}

总结

  • 装饰 class
  • 装饰 class 方法
  • react-redux 和 Angular

04-AOP

介绍

AOP - Aspect Oriented Programming 面向切面编程

简单来说:业务和系统基础功能分离,用 Decorator 很合适

在这里插入图片描述

PS:AOP 和 OOP 并不冲突

实现 log

function log(target: any, key: string, descriptor: PropertyDescriptor) {
    const oldValue = descriptor.value // fn1 函数

    // 重新定义 fn1 函数
    descriptor.value = function () {
        console.log(`记录日志...`)
        return oldValue.apply(this, arguments)
    }
}

class Foo {
    @log // 不影响业务功能的代码,只是加了一个 log 的“切面”
    fn1() {
        console.log('业务功能1')
    }
}

const f = new Foo()
f.fn1()

05-总结

内容回顾

  • 概念介绍 + 解决的问题
  • 代码演示和 UML 类图
  • 使用场景:ES Decorator 和 AOP

重要细节

  • tsconfig.json 中 experimentalDecorators: true
  • 装饰器如何传递参数(装饰器工厂函数)
  • 装饰器函数的第三个参数:属性描述符

注意事项

  • Angular 只为演示,国内应用不多
  • AOP 先了解概念,不急于详细掌握(需要长时间才能理解,如 OOP )

根据你的理解,写一个 AOP 的示例:执行一个函数时自动打印一条日志。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值