设计模式-装饰器模式

参考:https://www.cnblogs.com/pluto-charon/p/16030199.html

​ 装饰器模式又叫包装模式,数据结构型模式;是指在不改变现有对象结构的情况下,动态的给改对象增加一些职责(即增加其额外功能)的模式。

​ 在星巴克咖啡店,有美式咖啡(LongBlack)、无因咖啡(Decaf)、意大利农咖啡(Espresso)等不同的咖啡种类,也可以添加牛奶(Milk)、豆浆(Soy)、巧克力(Chocolate)等调料。下面我们就以这个为例子讲解装饰器模式。

​ 使用传统的方式设计,就是每一种咖啡和每一种调料都写一个类,都继承自Drink抽象类。这样的缺点是每增加一个单品咖啡,或者增加一个新的调料,类的数量就会成倍增加,形成类爆炸。

装饰器模式的UML类图:

PFZ6_B___0_P_~1O81HEE_H.png

从上图可以看到装饰器模式主要有抽象构件角色、具体构件角色、抽象装饰角色、具体装饰角色等四个角色:

  • 抽象构件角色:给出一个抽象接口,以规范准备接收附加责任的对象
  • 具体构件角色:实现抽象构件,并通过装饰角色为其添加一些职责
  • 抽象装饰角色:继承抽象构件角色,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
  • 具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任

使用装饰器模式完成上面例子的UML类图:

_UD_5BJ@C2LV8H4_MXBGOSS.png

抽象构件角色:

1
2
3
4
5
6
7
8
9
@Data
public abstract class Drink {

public String desc;

private float price = 0.0f;

public abstract float cost();
}

具体构建角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}

public class Decaf extends Coffee{
public Decaf() {
setDesc("无因咖啡");
setPrice(1.0f);
}
}

public class LongBlack extends Coffee{
public LongBlack() {
setDesc("美式咖啡");
setPrice(5.0f);
}
}

抽象装饰角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Decorator extends Drink{

/**
* 使用聚合的方式
*/
private Drink drink;

public Decorator(Drink drink) {
this.drink = drink;
}

@Override
public float cost() {
return super.getPrice()+drink.cost();
}

@Override
public String getDesc() {
return super.getDesc()+"&&"+drink.getDesc();
}
}

具体装饰角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDesc("牛奶");
setPrice(2.0f);
}
}

public class Soy extends Decorator{
public Soy(Drink drink) {
super(drink);
setDesc("豆浆");
setPrice(1.5f);
}
}

测试:

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
Drink longBlack = new LongBlack();
System.out.println(longBlack.getDesc()+",费用:"+longBlack.getPrice());
longBlack = new Milk(longBlack);
System.out.println(longBlack.getDesc()+",费用:"+longBlack.cost());
longBlack = new Soy(longBlack);
System.out.println(longBlack.getDesc()+",费用:"+longBlack.cost());
}
}

装饰器模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

优点:

  • 装饰器模式是对继承的有利补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同的效果
  • 装饰器模式完全遵循“开闭原则”

缺点:

  • 装饰器模式会增加许多子类,过度使用会增加程序的复杂性

应用场景

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销。