一、场景描述

(一)问题

系统中最初使用Crystal Report(水晶报表)工具生成报表,并将报表发送给客户端查看,此时定义一CrystalReport工具类即可完成水晶报表的生成工作。

后续报表工具增加SSRS报表(SQL Server Report Service),此时可定义SSRSReport工具类完成SSRS报表的生成工作;

并定义Report接口,重构两报表工具类实现Report接口,客户端通过接口统一调用。

需求接着变更,报表工具需要在发送给客户端(以PDF格式)的同时,可导出一份Word可编辑版本。对此可定义CrystalReportPDF和CrystalReportWord、SSRSReportPDF、SSRSReportWord四个工具类实现相应的功能。

需求继续变更,报表工具需要在发送给客户端(以Word格式或PDF格式)的同时,可将报表存储到文件服务器(File System)上。对此可定义CrystalReportPDF、CrystalReportPDF2FS、CrystalReportWord、CrystalReportWord2FS……。

需求仍然在变更,报表工具增加了Fine Report(帆软报表),要求在输出的同时,存储到FTP服务器上、邮件发送给指定用户,输出格式需要Excel……

(二)解决方案

针对此场景,之前的办法很痛苦(通过定义类实现接口),此时可应用装饰器模式。

报表工具一共有三种,即Crystal Report、SSRS和Fine Report,因此可定义三个类实现Report接口。

导出为Word版本、导出为Excel版本、存储到文件系统、存储到FTP服务器上、邮件发送用户等可理解为报表工具的装饰。

因此可以定义各种ReportGenerator的装饰类,用于给报表工具类(三种中具体的某一种)添加装饰(可以添加多种装饰,并可多次添加)。

据此定义装饰类父类ReportGenerator,使其持有Report接口对象,并实现Report接口,至此则可对持有的对象添加装饰,并最终将装饰好的方法发布出去。

接着为不同类型的装饰器定义类,继承父类ReportGenerator,例如定义ExportWordReport类,使其可输出Word格式报告,定义StorageReport2FTP类,使其将报表存储到FTP服务器。

调用端,可创建某类型的报表(三种报表服务中的一种),并调用不同的装饰器类组合,实现动态扩展类功能,例如调用ExportWordReport、ExportExcelReport和StorageReport2FTP,则实现将报表输出为Word和Excel,并存储到FTP服务器。

另外,假设有一装饰功能,发送报告生成通知给用户,发送方式有Email、短信、APP通知等,由于发送时需要对消息进行处理,因此可定义一公用的生成消息装饰类,此时装饰类可定义父子类不断继承。

装饰器的优点是动态的扩展了类功能(相比定义具体的类去实现接口),将装饰功能抽象为装饰类,减少了实现类的数量,降低了复杂度,也更符合对象的自然情况(个人认为区分装饰类与子类的方法是,装饰可以加也可以不加,可以加一个也可以重复添加,而子类则有且仅有一个;比如吃饭时,面条、馒头、米饭作为主食,一般人是选择其中一种,则可实现为子类,而西红柿炒鸡蛋、黄瓜炒木耳等炒菜可以添加两份或三份,甚至可以要两份西红柿炒鸡蛋,因此炒菜可以作为装饰类)。

装饰器的缺点是由于装饰的层数可以不等,因此在排查问题等方面较复杂,好比一堵墙上添了N层墙纸、刷了N多涂料,现在墙上裂了缝到底是哪里出了问题就有点难排查了。

二、示例代码

接口:

package lims.designpatterndemo.decoratedemo;

public interface Report {
public String generateReport();
}

Crystal Report报表工具类:

package lims.designpatterndemo.decoratedemo;

public class CrystalReport implements Report {

    @Override
public String generateReport() {
return "Generate Report using Crystal Report!";
} }

SSRS报表工具类:

package lims.designpatterndemo.decoratedemo;

public class SSRSReport implements Report {

    @Override
public String generateReport() {
return "Generate Report using Sql Server Report Service!";
}
}

Fine Report报表工具类:

package lims.designpatterndemo.decoratedemo;

public class FineReport implements Report {

    @Override
public String generateReport() {
return "Generate Report using Fine Report!";
} }

装饰类父类:

package lims.designpatterndemo.decoratedemo;

public class ReportGenerator implements Report {
//持有接口
private Report report;
public ReportGenerator(Report report){
this.report = report;
} @Override
public String generateReport() {
return report.generateReport();
} }

输出Word报告装饰类:

package lims.designpatterndemo.decoratedemo;

public class ExportWordReport extends ReportGenerator{
public ExportWordReport(Report report) {
super(report);
}
public String generateReport() {
return super.generateReport() + " Export to Word Format!";
}
}

输出Excel报告装饰类:

package lims.designpatterndemo.decoratedemo;

public class ExportExcelReport extends ReportGenerator{
public ExportExcelReport(Report report) {
super(report);
}
public String generateReport() {
return super.generateReport() + " Export to Excel Format!";
}
}

存储报告到FTP服务器装饰类:

package lims.designpatterndemo.decoratedemo;

public class StorageReport2FTP extends ReportGenerator{
public StorageReport2FTP(Report report) {
super(report);
}
public String generateReport() {
return super.generateReport() + " Storage Report to FTP Server!";
}
}

动态调用:

package lims.designpatterndemo.decoratedemo;

public class DecorateDemo {
public static void main(String args[]){
Report report = new CrystalReport();
report = new ExportWordReport(report);
report = new ExportExcelReport(report);
report = new StorageReport2FTP(report);
System.out.println(report.generateReport());
}
}

输出结果:

Generate Report using Crystal Report! Export to Word Format! Export to Excel Format! Storage Report to FTP Server!

发送通知消息装饰类:

package lims.designpatterndemo.decoratedemo;

public class SendReport extends ReportGenerator {

    public SendReport(Report report) {
super(report);
} public String generateReport() {
return super.generateReport() + " Send Report!";
}
}

发送消息到Email装饰类:

package lims.designpatterndemo.decoratedemo;

public class SendReport2Email extends SendReport {

    public SendReport2Email(Report report) {
super(report);
} public String generateReport() {
return super.generateReport() + " send to Email!";
}
}

调用测试:

package lims.designpatterndemo.decoratedemo;

public class DecorateDemo {
public static void main(String args[]){
Report report = new CrystalReport();
report = new ExportWordReport(report);
report = new ExportExcelReport(report);
report = new StorageReport2FTP(report);
report = new SendReport2Email(report);
System.out.println(report.generateReport());
}
}

输出结果:

Generate Report using Crystal Report! Export to Word Format! Export to Excel Format! Storage Report to FTP Server! Send Report! send to Email!

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2ui9qt2awpwkg

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2ui9qt2awpwkg

Java设计模式(七)Decorate装饰器模式的更多相关文章

  1. 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...

  2. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  3. JAVA设计模式之【装饰者模式】

    JAVA设计模式之[装饰者模式] 装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用. 在软件设计中,对已有对象(新房)的功能进行扩展(装修). 把通用功能封装在装 ...

  4. Java设计模式之(七)——装饰器模式

    1.什么是装饰器模式? Attach additional responsibilities to an object dynamically keeping the same interface.D ...

  5. Java IO流以及装饰器模式在其上的运用

    流概述 Java中,流是一种有序的字节序列,可以有任意的长度.从应用流向目的地称为输出流,从目的地流向应用称为输入流. Java的流族谱 Java的 java.io 包中囊括了整个流的家族,输出流和输 ...

  6. 设计模式学习心得<装饰器模式 Decorator>

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...

  7. 设计模式入门之装饰器模式Decorator

    //装饰模式定义:动态地给一个对象加入一些额外的职责. //就添加功能来说.装饰模式比生成子类更为灵活 //这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合,而不是对象继承 //Compo ...

  8. java进阶系列之装饰器模式

    1.介绍 装饰器模式顾名思义就是装饰某个对象的,是一种结构型模式.装饰器模式允许向一个现有对象添加新的功能,同时不改变其结构,用户可以随意的扩展原有的对象.它是作为现有的类的一个包装.装饰器模式一方面 ...

  9. 设计模式のDecoratorPattern(装饰器模式)----结构模式

    一.产生背景 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装 ...

随机推荐

  1. 使用kafka connect,将数据批量写到hdfs完整过程

    版权声明:本文为博主原创文章,未经博主允许不得转载 本文是基于hadoop 2.7.1,以及kafka 0.11.0.0.kafka-connect是以单节点模式运行,即standalone. 首先, ...

  2. http.request请求及在node中post请求参数解析

    Post请求 var http=require('http'); var qs=require('querystring'); var post_data={a:123,time:new Date() ...

  3. spring - boot 监控管理模块搭建

    Spring-Actuator是Spring-boot对应用监控的集成模块,提供了我们对服务器进行监控的支持,使我们更直观的获取应用程序中加载的应用配置.环境变量.自动化配置报告等. 使用Spring ...

  4. 【Python】 高级文件操作 shutil

    shutil 很多时候,我想要对文件进行重命名,删除,创建等操作的时候的想法就是用subprocess开一个子进程来处理,但是实际上shutil可以更加方便地提供os的文件操作接口,从而可以一条语句搞 ...

  5. C++关联容器知识总结

    C++的容器类型可以分为顺序容器和关联容器两大类.顺序容器的知识可以参看我上篇的随笔<C++顺序容器知识总结>.关联容器支持通过键值来高效的查找和读取元素,这是它和顺序容器最大的区别.两种 ...

  6. 安装php扩展 ffmpeg-php

    环境: CentOS 6.5 PHP5.6 安装前php 已加载GD 模块(yum install php-gd)1.添加ffmpeg和ffmpeg-devel源 cat > /etc/yum. ...

  7. jQuery学习笔记 .addClass()/.removeClass()简单学习

    使用jQuery或javaScript来动态改变页面中某个或部分元素的样式,为了实现这样的功能,我们往往都是使用jQuery或javaScript来控制HTML中DOM的类名(class)从而实现增加 ...

  8. 【剑指Offer学习】【面试题:二维数组中的查找】PHP实现

    最近一直看剑指Offer.里面很多算法题.于是就想着用PHP来显示一下. 题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序. 请完成一个函数,输入这样的 ...

  9. tensorflow安装过程-(windows环境下)---详解(摆平了很多坑!)

    一, 前言:本次安装tensorflow是基于Python的,安装Python的过程不做说明(既然决定按,Python肯定要先了解啊):本次教程是windows下Anaconda安装Tensorflo ...

  10. java通过数据库连接池链接oracle

    开发工具:Eclipse J2EE 3.6 运行环境:jdk1.6 部署环境:Tomcat7 数据库连接池用的是dbcp,网上download下来的三个jar包. 把数据库连接池包和jdbc的包放到t ...