装饰模式-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)
}
}