装饰模式-designer-decorator-ts

装饰者模式:

npm init -y
npm i vite -D
'dev':'vite'
npm run dev

装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能。
为了不改变原有的对象,我们可以把原对象放入到一个新的对象中以形成一个聚合对象。并且这些对象都有相同的接口。当我们使用这个装饰器对象时,会顺着请求链请求到上一个对象。对于用户来说,这个装饰器对象是透明的,用户可以依照这种方式一层一层的递归下去。

es5:

class Shape {
  constructor(name) {
    this.name = name
  }

  draw() {
    console.log(`draw ${this.name}`)
  }
}

class ColorDecorator {
  constructor(shape) {
    this.shape = shape
  }

  draw() {
    this.setColor()
    this.shape.draw()
  }

  setColor() {
    console.log(`color the ${this.shape.name}`)
  }
}

let circle = new Shape('circle')
circle.draw()

let decorator = new ColorDecorator(circle)
decorator.draw()

通过 ColorDecorator 对象实现了一个相同的 draw()方法,并在其中封装了 setColor()这个额外的职责方法,用户同样在调用 draw()方法时,也调用了上一个对象 Shape 的 draw()方法。我们可以以此类推,给对象添加上色,设置边框的一系列职责。

实际上,装饰器也是一种包装器,把上个对象包装到某个对象中,层层包装

es7 装饰着模式:

class Shape {
  @log
  setColor(color) {
    return color
  }
}

function log(target, name, descriptor) {
  var oldValue = descriptor.value

  descriptor.value = function () {
    console.log(`Calling ${name} width `, arguments)
    return oldValue.apply(this, arguments)
  }

  return descriptor
}

const shape = new Shape()
const color = shape.setColor('red')
console.log('color', color)

添加了一个 log 的装饰器,当执行 setColor()方法时,先去执行 log 方法,会自动打印日志,之后再返回到类的 setColor 方法

项目构成

app.ts,组件入口文件;
index.ts:具体 TodoList 组件实现类文件,具体实现操作的步骤,增加,删除,切换状态;
todoEvent.ts 事件处理函数;
template.ts:view 模版视图部分

组件入口模块文件:app.ts

/**
 * 文件的入口文件:
 * 固定写法:一个模块
 */
;((doc) => {
  //1.获取dom,元素的获取
  //2.定义入口文件执行函数
  const init = (): void => {
    //3.执行绑定事件处理函数
    bindEvent()
  }
  //具体时间处理函数:
  function bindEvent(): void {
    //事件监听
  }
  //具体事件处理函数:

  //4.入口文件执行
  init()
})(document)

入口文件详解

作为一个组件的入口,主要做一下工作: 1.定义模块,在模块中获取 dom 元素,给 dom 元素绑定事件监听 2.初始化创建类的实例, 3.在绑定的事件处理中调用类的方法进行处理

todoList 类:

主要是 dom 操作
根据需求创建新增,删除,改变切换状态的操作 dom 的函数,定义好之后在类的方法具体的 dom 操作的函数上添加对应的装饰器函数

class TodoList {
  //单例模式
  private static instance: TodoList
  private oTodoList: HTMLElement

  constructor(oTodoList: HTMLElement) {
    this.oTodoList = oTodoList
  }
  //创建单例的方法
  public static createInstance(oTodoList: HTMLElement) {
    if (!TodoList.instance) {
      TodoList.instance = new TodoList(oTodoList)
    }
    return TodoList.instance
  }
  /**
   * 增加的方法
   * @param todo
   * 具体步骤:创建外层容器,添加类,调用view视图层代码片段追加到创建的容器,再把容器追加到类初始化的容器中
   */
  public addItem(todo: ITodo) {
    const oItem: HTMLElement = document.createElement('div')
    oItem.className = 'todo-item'
    oItem.innerHTML = todoView(todo)
    this.oTodoList.appendChild(oItem)
  }

  public removeItem(id: number) {
    const oItems: HTMLCollection = document.getElementsByClassName('todo-item')

    Array.from(oItems).map((oItem) => {
      const _id = parseInt(oItem.querySelector('button').dataset.id)
      if (id === _id) {
        oItem.remove()
      }
    })
  }

  public toggleCompleted(id: number, completed?: boolean) {
    const oItems: HTMLCollection = document.getElementsByClassName('todo-item')
    Array.from(oItems).map((oItem) => {
      const _id = parseInt(oItem.querySelector('input').dataset.id)
      // const _checked = oItem.querySelector('input').checked
      if (_id === id) {
        const oContent: HTMLElement = oItem.querySelector('span')
        oContent.style.textDecoration = completed ? 'line-through' : 'none'
      }
    })
  }
}

装饰器函数:

装饰器函数主要负责数据的操作
通过具体的方法操作在对应的函数处理数据

let todoData: ITodo[] = []

/**
 * 添加函数的装饰器:
 * @param target 当前装饰函数的容器 xxx.prototype
 * @param methodName 被装饰的函数名称
 * @param descriptor 描述的信息,.value是被装饰执行的函数
 * 注意函数的定义,不要使用箭头函数,会改变传值
 */
export function addTodo(
  target: any,
  methodName: string,
  descriptor: PropertyDescriptor
) {
  const _origin = descriptor.value
  descriptor.value = function (todo: ITodo) {
    const _todo: ITodo | null = todoData.find(
      (td: ITodo) => td.content === todo.content
    )
    if (_todo) {
      alert('该项以添加...')
      return
    }
    todoData.push(todo)
    _origin.call(this, todo)
    console.log('todoData:', todoData)
  }
}

/**
 * 删除数据装饰器:
 * @param target
 * @param methodName
 * @param descriptor
 */
export function removeTodo(
  target: any,
  methodName: string,
  descriptor: PropertyDescriptor
) {
  const _origin = descriptor.value
  descriptor.value = function (id: number) {
    todoData = todoData.filter((todo: ITodo) => todo.id !== id)
    _origin.call(this, id)
    console.log('todoData:', todoData)
  }
}

/**
 * 改变选中状态
 * @param target
 * @param methodName
 * @param descriptor
 */
export function changeCompleted(
  target: any,
  methodName: string,
  descriptor: PropertyDescriptor
) {
  const _origin = descriptor.value
  descriptor.value = function (id: number) {
    todoData.map((todo: ITodo) => {
      if (todo.id === id) {
        todo.completed = !todo.completed
        _origin.call(this, id, todo.completed)
      }
      return todo
    })
    console.log('changeCompleted:', todoData)
  }
}
posted @ 2021-04-18 23:01  seafwg  阅读(102)  评论(0编辑  收藏  举报