• 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.装饰模式可以动态扩展一个类的功能和行为。

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

  • 相关阅读:
    调试 XPTable
    适合IT经理的编程语言
    请问我如何在一个webBrowser控件中加载一个 html格式的字符串 _NET技术 C#
    DotNetBar 教程
    思梅
    无家可归的苦
    思霞
    思兰
    十大因素——造就优秀的董事长
    思萍
  • 原文地址:https://www.cnblogs.com/sunshine798798/p/10078569.html
Copyright © 2020-2023  润新知