一、場景描述 (一)問題 系統中最初使用Crystal Report(水晶報表)工具生成報表,並將報表發送給客戶端查看,此時定義一CrystalReport工具類即可完成水晶報表的生成工作。 後續報表工具增加SSRS報表(SQL Server Report Service),此時可定義SSRSRep ...
一、場景描述
(一)問題
系統中最初使用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