Java设计模式之装饰模式

前言

什么是装饰模式呢?装饰模式是指动态地给一个对象添加额外的职责。因此装饰,也叫对象的包装。

装饰模式示例

互联网行业的迅猛发展,涌现了不计其数的Java开发工程师,想想必大家都很清楚,需求有产品经理把控,设计和开发一般是开发人员开发。软件测试由测试工程师负责。部署上线一般来说是实施人员,但是有很多公司自用的系统,往往是由项目负责人负责上线的工作。我早年大学还没毕业,在江苏一家公司工作,我记得当时我们的整套软件产品都是由我们的技术负责人上线。而且一般来说项目的开发一般有周期,同时分阶段。那就涉及到线上环境版本更新的频率,一般,一周或两周更新依次版本的。

我们就以项目部署上线来阐述装饰模式。部署上线,核心的流程有:

1.拿到测试人员的软件测试质量报告。

2.根据测试报告从下游系统开始依次往上进行升级。

于是我们首先来创建一个抽象类,用来定义项目版本更新的流程。我就命名为AbstractCompose.java。代码如下所示:

1 package com.example.pattern.decorator;
2 
3 public abstract class AbstractCompose {
4 
5     public abstract void report();
6 
7     public abstract void update();
8 }

第5行:拿到测试人员测试通过的质量报告。

第7行:线上环境项目版本更新。

既然有抽象类,我们再来看具体执行的流程,这个流程的实现类继承自 流程抽象类 代码如下所示:

 1 package com.example.pattern.decorator;
 2 
 3 public class PublicCompose extends AbstractCompose {
 4     @Override
 5     public void report() {
 6         System.out.println("测试通过的测试质量报告");
 7     }
 8 
 9     @Override
10     public void update() {
11         System.out.println("项目更新到最新版本");
12 
13     }
14 }

接下来,是我们的开发人员,对这个流程执行。因此我们定义一个Developer作为开发者,这个开发者的角色是也是我们习惯所说的场景类。

1 public class Developer {
2 
3     public static void main(String[] args) {
4         AbstractCompose compose = new PublicCompose();
5         compose.report();
6         compose.update();
7     }
8 }

这段代码特别的简单,也是对项目版本更新这段流程的直接表达。我们来看一下执行结果:

测试通过的测试质量报告
项目更新到最新版本

但是因为开发者个性的差异,比如有些开发者在执行版本更新之前,需要去一下wc。执行版本更新之后,需要团队聚餐。那好办,找一个子类来继承PublicCompose。

 1 public class PublicDetailCompose extends PublicCompose {
 2 
 3 
 4     public void toilet() {
 5         System.out.println("开发小哥在WC");
 6     }
 7 
 8     public void dinner() {
 9         System.out.println("大家在共进晚餐");
10     }
11 
12     @Override
13     public void update() {
14         toilet();
15         super.update();
16         dinner();
17     }
18 }

第13行-17行,通过重写父类的update方法,同时在调用父类的方法前后,修饰上WC和聚餐的动作。那么,开发小哥,也就是场景类需要做一下改动:

1 public class Developer {
2 
3     public static void main(String[] args) {
4         AbstractCompose compose = new PublicDetailCompose();
5         compose.report();
6         compose.update();
7     }
8 }

我们再执行一下,效果如下所示:

1 测试通过的测试质量报告
2 开发小哥在WC
3 项目更新到最新版本
4 大家在共进晚餐

第2行和第4行,装饰到我们的uodate方法中。

继承确实能够去解决一些问题。但是也出现了一些问题:

1.开发小哥WC可能在拿到测试报告之前,或者是在更新完之后。

2.聚餐可能发生在更新之前,也有可能在报告之前。

3.而WC这个流程节点,根据开发者的喜好不同,又可以分成两类,第一类:有的开发者喜欢WC之后洗手,第二类:有的开发者喜欢WC后不洗手直接回办公室等等。

这么多的场景,继续增加子类继承来扩展,于是出现了类的爆炸,类的数量激增。在面向对象语言 的开发中,如果继承的数量超过两层就应该要去考虑设计是不是合理。那有什么办法解决吗,有方法,我们来定义另一个装饰的接口,专门用于描述装饰的动作。我们先定义一个装饰的抽象类。这个抽象类必须要继承流程抽象类AbstractCompose。

 1 public abstract class AbstractDecorator extends AbstractCompose{
 2 
 3     private AbstractCompose compose;
 4 
 5     public AbstractDecorator(AbstractCompose compose) {
 6         this.compose = compose;
 7     }
 8 
 9     @Override
10     public void report() {
11         this.compose.report();
12     }
13 
14 
15     @Override
16     public void update() {
17         this.compose.update();
18     }
19 }

第1行,这个装饰的抽象类,必须要继承流程的抽象类AbstractCompose。

第3行,必须要持有流程类的引用。

第10行-18行,还是委托给想要装饰的类的方法。report和update。

AbstractDecorator抽象类存在有什么意义呢?就是希望让AbstractDecorator的子类来封装和装饰我们 要去装饰的那一个类,也就是PublicCompose。

①首先封装一个wc的装饰器:

 1 public class PublicToiletDecorator extends AbstractDecorator {
 2 
 3 
 4     public PublicToiletDecorator(AbstractCompose compose) {
 5         super(compose);
 6     }
 7 
 8     public void toilet () {
 9         System.out.println("开发小哥在WC,并且没有洗手回到办公室 ");
10     }
11 
12 
13     @Override
14     public void update() {
15         toilet();
16         super.update();
17     }
18 }

第14行,override了update方法,同时在这个方法之前,增加了要装饰的方法toilet。

②我们再来创建一个聚餐的装饰类:

 1 public class PublicDinnerDecorator extends AbstractDecorator {
 2 
 3 
 4     public PublicDinnerDecorator(AbstractCompose compose) {
 5         super(compose);
 6     }
 7 
 8     public void dinner () {
 9         System.out.println("团队一起聚餐 ");
10     }
11 
12 
13     @Override
14     public void update() {
15         super.update();
16         dinner();
17     }
18 }

第15行,override了update方法,同时在这个方法之后,增加了要装饰的方法dinner。

我们先执行一下:

1 测试通过的测试质量报告
2 开发小哥在WC,并且没有洗手回到办公室 
3 项目更新到最新版本
4 团队一起聚餐 

是不是很神奇,如果想继续装饰其他的行为,是不是就变得比较简单了。

装饰模式的角色

1.想要装饰的类的抽象或者接口A

2.具体的装饰类B,这个类继承抽象类,或者实现接口A

3.抽象的装饰接口或者抽象类C,同时继承或者实现A.同时持有A的引用

4.具体的装饰类。这个类继承或者实现自C

装饰模式的优点和缺点

1.装饰类和被装饰类相互独立。

2.装饰模式是继承的一种代替方案。

3.装饰模式可以动态扩展一个类的功能和行为。

以上是装饰模式的优点,同时在装饰层数过多时,排查问题比较复杂。因此,尽量减少装饰类的数量来降低系统的复杂度。

原文地址:https://www.cnblogs.com/candies123/p/10078569.html