Skip to content
On this page

装饰器模式

在不改变原对象的基础上,通过对其进行包装拓展,使原有的对象可以满足用户的更复杂需求。

装饰器的应用场景

按钮的封装,未登录点击时弹出未登录的提示

javascript
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>装饰器</title>
</head>

<body>
  <button id="open">打开</button>
  <button id="close">关闭</button>
  <script>
    const Modal = (function () {
      let instance = null;
      return function () {
        if (!instance) {
          instance = document.createElement('div');
          instance.innerHTML = '你还未登录!';
          instance.style.display = 'none';
          instance.id = 'modal';
          document.body.appendChild(instance);
        }
        return instance;
      }
    })()
    document.getElementById('open').onclick = function () {
      const modal = new Modal()
      modal.style.display = 'block'
    }
    document.getElementById('close').onclick = function () {
      const modal = document.getElementById('modal')
      if (modal) {
        modal.style.display = 'none'
      }
    }
  </script>
</body>

</html>

现在要添加新的需求,打开按钮的时候把 button 文字改成:快去登录。

装饰器模式

先拆分旧逻辑

javascript
function openModal() {
  const modal = new Modal()
  modal.style.display = 'block'
}

添加新逻辑

javascript
function changeBtnText() {
  const open = document.getElementById('open')
  open.innerHTML = '快去登录'
}

function disableButton() {
  const open = document.getElementById('open');
  open.setAttribute('disabled', true);
}

function changeBtnState() {
  changeBtnText()
  disableButton()
}

这样直接调用方法

javascript
document.getElementById('open').onclick = function() {
      openModal();
      changeBtnState()
    }

如此一来,我们就实现了“只添加,不修改”的装饰器模式,使用 changeBtnState 的逻辑装饰了旧的按钮点击逻辑。以上是ES5中的实现,ES6中,我们可以以一种更加面向对象化的方式去写:

javascript
// 定义打开按钮
class OpenButton {
    // 点击后展示弹框(旧逻辑)
    onClick() {
        const modal = new Modal()
     modal.style.display = 'block'
    }
}

// 定义按钮对应的装饰器
class Decorator {
    // 将按钮实例传入
    constructor(open_button) {
        this.open_button = open_button
    }
    
    onClick() {
        this.open_button.onClick()
        // “包装”了一层新逻辑
        this.changeButtonStatus()
    }
    
    changeButtonStatus() {
        this.changeButtonText()
        this.disableButton()
    }
    
    disableButton() {
        const btn =  document.getElementById('open')
        btn.setAttribute("disabled", true)
    }
    
    changeButtonText() {
        const btn = document.getElementById('open')
        btn.innerText = '快去登录'
    }
}

const openButton = new OpenButton()
const decorator = new Decorator(openButton)

document.getElementById('open').addEventListener('click', function() {
    // openButton.onClick()
    // 此处可以分别尝试两个实例的onClick方法,验证装饰器是否生效
    decorator.onClick()
})