1、定义

在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案,属于结构型设计模式。

2、身边例子(反例写法)

山东煎饼果子例子、装修例子、促销打折例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package top.zhhades.design.decorator.v1; 
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
/**
* @author zhhades
*/
@Slf4j
public class Pancake {
public String makeBy() {
return "面粉";
}
public int cost() {
return 5;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package top.zhhades.design.decorator.v1; 
import lombok.ToString;
/**
* @author zhhades
*/
public class PancakeWithEgg extends Pancake {
@Override
public String makeBy() {
return super.makeBy() + " 加1个鸡蛋";
}
@Override
public int cost() {
return super.cost() + 2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package top.zhhades.design.decorator.v1; 
/**
* @author zhhades
*/
public class PancakeWithHam extends PancakeWithEgg {
@Override
public String makeBy() {
return super.makeBy() + " 加1个火腿肠";
}
@Override
public int cost() {
return super.cost() + 2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package top.zhhades.design.decorator.v1; 
import lombok.extern.slf4j.Slf4j;
/**
* @author zhhades
*/
@Slf4j
public class ClientTest {
public static void main(String[] args) {
Pancake pancake = new Pancake();
log.info("make by {} cost {}", pancake.makeBy(), pancake.cost());
Pancake pancakeWithEgg = new PancakeWithEgg();
log.info("make by {} cost {}", pancakeWithEgg.makeBy(), pancakeWithEgg.cost());
Pancake pancakeWithHam = new PancakeWithHam();
log.info("make by {} cost {}", pancakeWithHam.makeBy(), pancakeWithHam.cost());
}
}

UML类图

3、使用装饰器模式对代码进行重构

1
2
3
4
5
6
7
8
package top.zhhades.design.decorator.v2; 
/**
* @author zhhades
*/
public abstract class AbstractPancake {
protected abstract String makeBy();
protected abstract int cost();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package top.zhhades.design.decorator.v2; 
/**
* @author zhhades
*/
public abstract class AbstractPancakeDecorator extends AbstractPancake {
private AbstractPancake pancake;
public AbstractPancakeDecorator(AbstractPancake pancake) {
this.pancake = pancake;
}
@Override
protected String makeBy() {
return this.pancake.makeBy();
}
@Override
protected int cost() {
return this.pancake.cost();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package top.zhhades.design.decorator.v2; 
/**
* @author zhhades
*/
public class EggDecorator extends AbstractPancakeDecorator {
public EggDecorator(AbstractPancake pancake) {
super(pancake);
}
@Override
protected String makeBy() {
return super.makeBy() + " 加1个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package top.zhhades.design.decorator.v2; import org.springframework.util.Assert; 
/**
* @author zhhades
*/
public class HamDecorator extends AbstractPancakeDecorator {
public HamDecorator(AbstractPancake pancake) {
super(pancake);
}
@Override
protected String makeBy() {
return super.makeBy() + " 加1个火腿肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package top.zhhades.design.decorator.v2; 
/**
* @author zhhades
*/
public class Pancake extends AbstractPancake {
@Override
protected String makeBy() {
return "面粉";
}
@Override
protected int cost() {
return 5;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package top.zhhades.design.decorator.v2; 
import lombok.extern.slf4j.Slf4j;
/**
* @author zhhades
*/
@Slf4j
public class ClientTest {
public static void main(String[] args) {
AbstractPancake pancake = new Pancake();
pancake = new EggDecorator(pancake);
pancake = new HamDecorator(pancake);
log.info("make by {} cost {}", pancake.makeBy(), pancake.cost());
}
}

UML类图

4、装饰器适用场景

  • 扩展一个类的功能或给一个类添加附加职责
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销

5、装饰器优缺点

  • 优点
    • 继承的有力补充,比基础灵活,不改变原有对象的情况下给一个对象扩展功能
    • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
    • 符合开闭原则
  • 缺点
    • 出现更多的代码,更多的类,增加程序复杂性
    • 动态装饰时、多层装饰时会更复杂

6、装饰器在eureka、mybatis、jdk中的使用

mybatis

eureka

jdk

7、装饰模式的透明性

7.1透明

透明的装饰者模式要求具体构件角色、抽象装饰者角色与抽象构件角色的接口完全一致。透明的装饰者模式有很少的例子

7.2半透明

半透明的装饰者模式不要求完全保持一致,即抽象装饰角色与抽象构件的接口不完全一致。半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。

8、与适配器模式比较

8.1 概念区分

适配器是把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。适配器的本质是:转换匹配,复用功能。

装饰器模式是原有的功能不能满足现有的需求,对原有的进行增强

8.2 继承特点

适配器模式是用新接口调用原接口,原接口对系统是不可见、不可用的。

装饰器模式原封不动的使用原接口,装饰对象也通过原接口进行。增加新接口的装饰者模式可以认为是其变种–半透明装饰者

8.3 包装

装饰模式包装的是自己的兄弟类,隶属于同一个家族,有共同的祖先。

适配器模式则修饰非关联类,把一个非本家族的内伪装成本家族的对象。