前陣子手頭上有個功能需求為:產生多張excel報表,ex:報表一、報表二、報表三…等。
這邊我們透過設計模式的工廠模式(Factory Pattern)來trytry …
首先,我們會有一個報表底層=>_Service,並提供一個Method=>CreateObject(),負責建立報表實體。而_Service就是所謂的報表工廠,不管報表怎麼實作的,皆會透過CreateObject()建立出一個報表實體(報表類)。
public abstract class _Service { //實作物件 public static T CreateObject<T>(string TypeName, ReportParameterModel Parameter) { var theAssembly = typeof(T).Assembly; //取得類別 Type theType = theAssembly.GetType($"ReportService.{TypeName}"); if (theType == null) return default(T); return (T)Activator.CreateInstance(theType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { Parameter }, System.Globalization.CultureInfo.InvariantCulture, null); } }
但類似的報表有很多種。
所以再提供一個抽象類別=>_Xls,並且去繼承_Service,而這個抽象類別會提供產excel報表的Method=>CreateRPT(),負責處理產excel報表的過程。
public abstract class _Xls : _Service { //報表參數 public ReportParameterModel Parameter; internal Workbook excel; public _Xls(ReportParameterModel parameter) { Parameter = parameter; } //創建報表 public virtual byte[] CreateRPT() { excel = new Workbook(); Worksheet sheet = excel.Worksheets[0]; CreateSheets(); return Save(); } }
產excel報表的過程中,會有多種工作表(sheet),而每個報表擁有的工作表(sheet)皆不同,故我們提供一個抽象的Method=>CreateSheets(),交由子類別去決定要建立哪張工作表。
public abstract class _Xls : _Service { ...... //建立工作表 protected abstract void CreateSheets(); }
產excel報表的過程中,除了工作表建立,可根據參數決定報表格式為何,才將報表下載下來,故提供method=> Save()。
public abstract class _Xls : _Service { ....... protected virtual byte[] Save() { using (MemoryStream ms = new MemoryStream()) { if (Parameter.Type == "xlsx") { excel.Save(ms, SaveFormat.Xlsx); } else { excel.Save(ms, SaveFormat.ODS); } return ms.ToArray(); } } }
前述提到會有各種工作表,故這邊我們會有一個工作表介面=>WorkSheet,並提供取得工作表名稱的Method=>GetSheetName()。
public interface WorkSheet { string GetSheetName(); }
我們來實作一張工作表=>Report01Sheet,Report01Sheet會去繼承剛剛我們建立的工作表介面=>WorkSheet,並且實作GetSheetName()。
public class Report01Sheet : WorkSheet { public string GetSheetName(string name) { return name; } }
接下來,我們來實作報表類,報表一(Report01)會去繼承_Xls,並將複寫掉CreateSheets()。報表二也可以直接繼承報表一,再複寫掉CreateSheets(),若不複寫,則保有報表一的CreateSheets()。
public class Report01: _Xls { public Report01(ReportParameterModel P) : base(P) { } protected override void CreateSheets() { new Report01Sheet(Parameter.SheetName); } } public class Report02: Report01 { public Report02(ReportParameterModel P) : base(P) { } protected override void CreateSheets() { new Report02Sheet(Parameter.SheetName); } }
最後,我們就可以來建立出報表類的實體,但至於是哪個報表類,會根據傳入的TypeName來交由工廠處理。
當今天傳入Report01,就會產生報表一的實體,若傳入Report02,就會產生報表二的實體,以此類推。
建立報表類的實體後,呼叫CreateRPT(),即可下載報表。
下面的例子,最後會下載報表二。
public FileResult DownloadReport(ReportParameterModel model) { //報表一 var report = _Service.CreateObject<_Xls>("Report01", model); //報表二 var report = _Service.CreateObject<_Xls>("Report02", model); return File(report.CreateRPT(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "報表"); }
而以上的做法就是套用工廠模式的概念來實作,
何謂工廠模式?
工廠模式的實質是"定義一個建立物件的介面,但讓實現這個介面的類來決定實例化該類。工廠方法讓類別的實例化替代到子類中進行"。
也就是每張報表我們不需要知道他怎麼實作,但我們可透過工廠來決定他產生哪個報表類。
感謝大家收看,我們下集待續.....