Java设计模式(七)Decorate装饰器模式

一、场景描述

(一)问题

系统中最初使用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!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏喔家ArchiSelf

老曹眼中的Git

Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。大神就是大神,在开发了Linux之后,Git 是...

10230
来自专栏点滴积累

geotrellis使用(三十二)大量GeoTiff文件实时发布TMS服务

前言 在上一篇文章中我讲了如何直接将Geotiff文件发布为TMS服务,在其中只讲了单幅Geotiff的操作,其实单幅这种量级的数据对Geotrellis来说就...

32970
来自专栏腾讯IVWEB团队的专栏

一次 Node.js 内存溢出

因为内存上限设置不合理,引起的内存溢出问题。之前压测时候只关注了是否存在内存泄露与cpu占用,而忽视了内存占用这个问题。对于部署服务时,要根据机器的内存上限以及...

71000
来自专栏PhpZendo

掌握 Laravel 的测试方法

不管你承认与否在研发一款产品时,软件测试对项目而言意义重大,然而是测试通常被我们视而不见。这篇文章我们主要研究 Laravel 框架的测试方法。

41110
来自专栏散尽浮华

Gerrit日常操作命令收集

Gerrit代码审核工具是个好东西,尤其是在和Gitlab和Jenkins对接后,在代码控制方面有着无与伦比的优势。 在公司线上部署了一套Gerrit系统,在日...

25460
来自专栏Petrichor的专栏

git:git commit 书写格式

  正如 git add 的作用是将文件放入暂存区, git commit 的作用是将修改提交到分支上。

47920
来自专栏逸鹏说道

[git]撤销的相关命令:reset、revert、checkout

基本概念 工作区 暂存区 本地版本仓库 远程版本仓库 如果不清晰上面的四个概念,请查看廖老师的git教程 这里我多说几句:最开始我使用git的时候,我并不明白我...

31050
来自专栏WindCoder

《Linux内核分析》之分析system_call中断处理过程实验总结

先占个位置,在实验楼做实验,刚做完一半忘了延续时间,结果之前写的代码神马的全没了。让我先去角落哭会,总结明天再写。2015-04-04

9610
来自专栏PhpZendo

深入研究 Laravel ORM (Laravel Eloquent) 系统 (一) – 概要

本文作为 Laravel ORM 系统的研究开篇,主要对 Laravel ORM 系统的主要功能、依赖的第三方类库、系统的目录结构及对应目录所实现的功能,进行解...

90020
来自专栏Golang语言社区

设计Go API的管道使用原则

管道是并发安全的队列,用于在Go的轻量级线程(Go协程)之间安全地传递消息。总的来讲,这些原语是Go语言中最为称道的特色功能之一。这种消息传递范式使得开发者可以...

37560

扫码关注云+社区

领取腾讯云代金券