长大后想做什么?做回小孩!

0%

装饰器模式

装饰器模式(Decorator Pattern)提供了一种给对象灵活地、动态地添加额外功能的思路,装饰器模式是一种结构型模式。

日常开发中,经常需要对一个旧的类进行扩展,但是又希望遵循开闭原则不直接修改原本的类,于是我们可以创建一个子类并继承原类,并进行新增或者重写。如果扩展的次数不多,采用这种方式就可以方便快捷的完成扩展。

但是这种继承的方式是静态的,会随着扩展次数的增多,子类越来越多的同时继承关系越来越复杂,并且不同版本之间的类严重耦合。

经验和直觉告诉我们应该可以在 原有类扩展 之间增加一层东西,从而降低上述的耦合。

这层东西,就是装饰器(Decorator),它可以抽象的理解成套在被装饰类外面的一层壳子,可以在这层壳子上增加各种装饰,从而达到扩展被装饰类的效果。

装饰器模式,相较于上面继承方法,是一种动态的扩展,耦合度大大降低,装饰类和被装饰类可以各自发展。

正文

LOL 中有不同的英雄,英雄都可以看做是被装饰类;每个英雄可以买不同的装备,装备可以看作是装饰。下面依据这种概念来进行举例:

wabsv6.png

实现,英雄(被装饰类)接口:

1
2
3
4
5
6
7
//英雄接口
public interface Hero {
//攻击力
Integer showAttackPower();
//背包中的装备
String showBackpack();
}

英雄(被装饰类)实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//实现两个英雄:提莫和辛德拉
public class Timo implements Hero {
@Override
public Integer showAttackPower() {
return 55;
}
@Override
public String showBackpack() {
return "腐败药水";
}
}
public class Sindra implements Hero {
@Override
public Integer showAttackPower() {
return 53;
}

@Override
public String showBackpack() {
return "多兰两红";
}
}

装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//装饰器
public abstract class Decorator implements Hero {
private Hero hero;
//通过构造方法传入被装饰类
public Decorator(Hero hero) {
this.hero = hero;
}

@Override
public Integer showAttackPower() {
return hero.showAttackPower();
}

@Override
public String showBackpack() {
return hero.showBackpack();
}
}

装备(装饰类)实现:

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
//实现两个装备:帽子和面具
public class Hat extends Decorator {
public Hat(Hero hero) {
super(hero);
}

@Override
public Integer showAttackPower() {
return super.showAttackPower() + 80;
}

@Override
public String showBackpack() {
return super.showBackpack() + "、大帽子";
}
}
public class Mask extends Decorator {
public Mask(Hero hero) {
super(hero);
}

@Override
public Integer showAttackPower() {
return super.showAttackPower() + 60;
}

@Override
public String showBackpack() {
return super.showBackpack() + "、大面具";
}
}

看一下 IDEA 生成的类图:

wwMKW4.png

照例,依然是找一个 JDK 中装饰器模式的应用,IO 包中的输入流就是使用了装饰器模式,来看类图:

wwMuYF.png

输入流有多个子类:文件输入流、字节输入流等等。。同时还有一个装饰器(过滤输入流),同时有多个子装饰器:加校验、加缓存等等。。使用的方法:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
try {
InputStream inputStream = new FileInputStream("src/main/Decorator.java");
inputStream = new BufferedInputStream(inputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

装饰器模式在实际应用中还是有一定的难度的,需要结合需求灵活选择。装饰器模式的中心思想就是提供一种继承之外的方式,进而降低耦合,实现更加清晰地职责划分。


菜鸟本菜,不吝赐教,感激不尽!

更多题解源码和学习笔记:githubCSDNM1ng