装饰器设计模式

装饰器模式就是在不改变原有对象的基础上,将功能附加在对象上,提供了比继承更有扩展性的方案,继承的替代方案,子类可拓展功能,相当于对一个对象可以创建出不同行为的组合,得到更强大的对象。

角色

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

代码示例

想要清楚的了解装饰器代码的作用,用代码装饰器和不用代码装饰器来进行对比

我们要知道,装饰器的本质本身也是类增强和代理模式很像,或者说他就是另外一种特殊代理模式,具体区别我们后面会分析。

正常不用装饰器代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 饭
public class Meal {
// 返回饭描述
public String getDescription() {
return "饭";
}

//返回价格描述
public Integer getPrice() {
return 5;
}
}
// 饭+鸡蛋
public class EggMeal extends Meal {
public String getDescription() {
return super.getDescription() + "+鸡蛋";
}

public Integer getPrice() {
return 10 + super.getPrice();
}
}
// 饭+鸡蛋+火腿
public class EggHumMeal extends EggMeal {
@Override
public String getDescription() {
return super.getDescription() + "+火腿";
}

@Override
public Integer getPrice() {
return super.getPrice() + 3;
}
}
// 试想一下如果我后续还要在加一些 配料的话,那是不是又要创建一个类呢? 然后继续这样继承下去;
// 从上面我们也应该知道一些弊端,这样是不是又过于死板不够灵活,如果我要在饭和鸡蛋的基础上在加一个鸡蛋之类的是不是又要写一个类饭+鸡蛋+鸡蛋



用装饰器模式

我们要知道,装饰器的角色有那些:抽象构件,目标构件、抽象装饰器、具体装饰器。

但是我们也要具体情况具体分析,不是什么一拿着就按照上面的这个套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 具体构建 如果是比较复杂的话可以用抽象构建+具体构建 不然的话用可以直接用具体构建
// 饭
public class Meal {
// 返回描述
String getDescription() {
return "饭";
}

// 返回价格
Integer getPrice() {
return 5;
}
}
// 抽象装饰器
// 配料表
public abstract class Ingredients extends Meal {
private Meal meal;

public Ingredients(Meal meal) {
this.meal = meal;
}

@Override
String getDescription() {
// this 这个要注意
return this.meal.getDescription();
}

/**
* 利用组合模式来进行增强
*
* @return
*/
@Override
Integer getPrice() {
return this.meal.getPrice();
}
}
// 具体装饰器
// 火腿
public class Hum extends Ingredients {
public Hum(Meal meal) {
super(meal);
}

@Override
String getDescription() {
return super.getDescription() + "火腿";
}

@Override
Integer getPrice() {
return super.getPrice() + 2;
}
}
// 具体装饰器
// 鸡蛋
public class Egg extends Ingredients {
public Egg(Meal meal) {
super(meal);
}

@Override
String getDescription() {
return super.getDescription() + "鸡蛋";
}

@Override
Integer getPrice() {
return super.getPrice() + 3;
}
}

public static void main(String[] args) {
//饭 具体构建
Meal meal = new Meal();
System.out.println(meal.getDescription() + ":" + meal.getPrice() + "元");

//饭的基础上加火腿
meal = new Hum(meal);
System.out.println(meal.getDescription() + ":" + meal.getPrice() + "元");

//饭+火腿的基础上加鸡蛋
meal = new Egg(meal);
System.out.println(meal.getDescription() + ":" + meal.getPrice() + "元");

//饭+火腿+鸡蛋的基础上加鸡蛋
Meal eggTwo = new Egg(meal);
System.out.println(eggTwo.getDescription() + ":" + eggTwo.getPrice() + "元");

}

可以看到,使用装饰器模式的方法实现,与普通的继承方法实现,最大的区别就是一种配料只有一个类,而且在加配料的时候,也可以直接想加多少就加多少,不需要说一个鸡蛋一个类,两个鸡蛋也要创建一个类,这样可以带来比继承更加灵活的扩展功能,使用也更加方便。

总结

装饰器模式与代理模式对比:

参考文章

  • 装饰器模式就是一种特殊的代理模式。
  • 装饰器模式强调自身的功能扩展,用自己说了算的透明扩展,可动态定制的扩展;代理模式强调代理过程的控制。
  • 获取目标对象构建的地方不同,装饰者是从外界传递进来的,可以通过构造方法传递;静态代理是在代理类内部创建,以此来隐藏目标对象。

适用场景:

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

优点:

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

缺点:

  • 会出现更多的代码,更多的类,增加程序的复杂性。
  • 动态装饰时,多层装饰会更复杂。