# 装饰者模式

## 装饰者模式

> Decorator pattern

### 知识点一：定义

装饰者模式提供了对一个对象动态添加额外临时的功能职责（属性和方法）。注意的是装饰者添加的功能，对于原本的对象来说并不是必要的，否则，该功能应该添加到对象原型中。 装饰者模式一般用于修改现有系统，希望为对象添加额外功能，而不需要修改体层代码。 一些类库例如JQuery，也有使用类似模式。jQuery.extend()方法就是用来扩展jQuery内置对象，可以认为是装饰者模式一种实现。

### 知识点二：具体例子

![image](/files/-LRk4Vc-vXAXIQ8WJaGK)

```javascript
var User = function(name) {
    this.name = name;

    this.say = function() {
        log.add("User: " + this.name);
    };
}

var DecoratedUser = function(user, street, city) {
    this.user = user;
    this.street = street;
    this.city = city;

    this.say = function() {
        log.add("Decorated User: " + this.user.name + ", " +
                   this.street + ", " + this.city);
    };
    return this;
}

// logging helper

var log = (function() {
    var log = "";

    return {
        add: function(msg) { log += msg + "\n"; },
        show: function() { console.log(log); log = ""; }
    }
})();

function run() {

    var user = new User("Kelly");
    user.say(); //User: Kelly

    var decorated = new DecoratedUser(user, "Broadway", "New York");
    decorated.say(); //Decorated User: Kelly, Broadway, New York

    log.show(); 
}
run();
```

#### 例子分析

例子中，User类中实例被装饰者类DecoratedUser装饰了（增强了）。实例对象添加了一个地址属性。原来的属性方法则保持不变（this.name）

***Client*** 客户端，例子中的run 1. 负责实例化装饰者类

***Component*** 目标组件，例子中的 User类 1. 需要添加额外功能的类

***Decorator*** 目标组件，例子中的 DecoratedUser类 1. 包裹目标组件，引用目标组件实例 1. 定义符合目标组件接口的接口（定义额外参数） 1. 实现额外功能（添加成员）

### 知识点三：特点

优点： 1. 相当灵活，旧的代码如果需要添加新的行为，可以在上面进行包装或者“装饰”，不用修改原来的底层代码 1. 不必依靠大量子类实现一些功能 缺点： 1. 如果滥用，会极大复杂化程序框架，解决方法应使用大量注解和文档说明，同时要限制装饰者数量

## 抽象装饰者（Abstract Decorator）模式

装饰者模式增强版。可以把多种功能相似装饰者抽象出来成为一个类基，称为抽象装饰者。抽象装饰者本身也可以装饰原有目标组件。

### 知识点一：具体例子

```javascript
class User {
  constructor(name){
    this.name = name;
    this.classType = 'normal';
  }
  say(){
    log.add("User: " + this.name+ "; classType:" + this.classType);
  }
}

class UserVip extends User {
  constructor(name){
    super(name);
    this.classType = 'VIP';
  }
}

class UserDecorator {
  constructor(user,street){
    this.user = user;
    this.street = street;
  }
  getStreet(){
    return " Decorated User Street is: "+this.street
  }
  say(){
      const user = this.user;
      log.add("User: " + user.name+ "; classType:" + user.classType + this.getStreet());
  }
}

class CaseUserDecorator extends UserDecorator{
  constructor(user, street, city){
     super(user, street);
     this.city = city;
  }
  getCity(){
    return " caseDecorated User City is: "+this.city
  }
  say(){
    const user = this.user;
    log.add("User: " + user.name+ "; classType:" + user.classType + this.getStreet() + '; ' + this.getCity());
  }
}

// logging helper

var log = (function() {
    var log = "";

    return {
        add: function(msg) { log += msg + "\n"; },
        show: function() { console.log(log); log = ""; }
    }
})();

function run() {
    var user = new User("Mike");
    user.say(); //User: Mike; classType:normal

    var decoratedUser = new UserDecorator(user, "Broadway");
    decoratedUser.say(); // User: Mike; classType:normal Decorated User Street is: Broadway

    var userVip = new UserVip("Kelly");
    userVip.say(); //User: Kelly; classType:VIP

    var decoratedVipUser = new CaseUserDecorator(userVip, "Broadway", "New York");
    decoratedVipUser.say(); // User: Kelly; classType:VIP Decorated User Street is: Broadway;  caseDecorated User City is: New York

    log.show();
}
run();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wikinote.gitbook.io/js-pattern/javascript-she-ji-mo-shi/jie-gou-mo-shi/decorator-pattern.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
