咱們先來看代碼,有點長java
public class ExportExcel<T> { private HSSFWorkbook workbook; public ExportExcel() { this(new HSSFWorkbook()); } public ExportExcel(HSSFWorkbook workbook) { this.workbook = workbook; } public void exportExcel(Collection<T> dataset, OutputStream out) { exportExcel("測試POI導出EXCEL文檔", null, dataset, out, "yyyy-MM-dd"); } public void exportExcel(String[] headers, Collection<T> dataset, OutputStream out) { exportExcel("測試POI導出EXCEL文檔", headers, dataset, out, "yyyy-MM-dd"); } public void exportExcel(String[] headers, Collection<T> dataset, OutputStream out, String pattern) { exportExcel("測試POI導出EXCEL文檔", headers, dataset, out, pattern); } public void exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern) { // 生成一個表格 HSSFSheet sheet = workbook.createSheet(title); // 生成數據標題和數據行樣式 HSSFCellStyle rowTirtleStyle = getRowTitleStyle(); HSSFCellStyle rowDataStyle = getRowDataStyle(); //建立數據標題和數據行 createRowTitle(headers, sheet, rowTirtleStyle); createRowData(dataset, pattern, sheet, rowDataStyle); //寫入流 writeExecl(out); } /** * Description:寫入到OutputStream */ private void writeExecl(OutputStream out) { try { workbook.write(out); } catch (IOException e) { e.printStackTrace(); } } /** * Description: 產生數據行 */ private void createRowData(Collection<T> dataset, String pattern, HSSFSheet sheet, HSSFCellStyle rowDataStyle) { // 遍歷集合數據,產生數據行 Iterator<T> it = dataset.iterator(); int index = 0; while (it.hasNext()) { index++; HSSFRow row = sheet.createRow(index); T t = (T) it.next(); // 利用反射,根據javabean屬性的前後順序,動態調用getXxx()方法獲得屬性值 Field[] fields = t.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { HSSFCell cell = row.createCell(i); cell.setCellStyle(rowDataStyle); Field field = fields[i]; String fieldName = field.getName(); String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); try { Class tCls = t.getClass(); Method getMethod = tCls.getMethod(getMethodName, new Class[] {}); Object value = getMethod.invoke(t, new Object[] {}); // 判斷值的類型後進行強制類型轉換 String textValue = null; if (value instanceof Boolean) { boolean bValue = (Boolean) value; textValue = "男"; if (!bValue) { textValue = "女"; } } else if (value instanceof Date) { Date date = (Date) value; SimpleDateFormat sdf = new SimpleDateFormat(pattern); textValue = sdf.format(date); } else { // 其它數據類型都看成字符串簡單處理 textValue = value.toString(); } // 若是不是圖片數據,就利用正則表達式判斷textValue是否所有由數字組成 if (textValue != null) { Pattern p = Pattern.compile("^//d+(//.//d+)?$"); Matcher matcher = p.matcher(textValue); if (matcher.matches()) { // 是數字看成double處理 cell.setCellValue(Double.parseDouble(textValue)); } else { HSSFRichTextString richString = new HSSFRichTextString(textValue); HSSFFont font3 = workbook.createFont(); font3.setColor(HSSFColor.BLUE.index); richString.applyFont(font3); cell.setCellValue(richString); } } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } finally { // 清理資源 } } } } /** * Description: 產生表格標題行 */ private void createRowTitle(String[] headers, HSSFSheet sheet, HSSFCellStyle rowTirtleStyle) { HSSFRow row = sheet.createRow(0); for (int i = 0; i < headers.length; i++) { HSSFCell cell = row.createCell(i); cell.setCellStyle(rowTirtleStyle); HSSFRichTextString text = new HSSFRichTextString(headers[i]); cell.setCellValue(text); } } /** * Description:生成數據標題樣式 */ private HSSFCellStyle getRowTitleStyle() { HSSFCellStyle style = workbook.createCellStyle(); // 設置這些樣式 style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 生成一個字體 HSSFFont font = workbook.createFont(); font.setColor(HSSFColor.VIOLET.index); font.setFontHeightInPoints((short) 12); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 把字體應用到當前的樣式 style.setFont(font); return style; } /** * Description:生成數據行樣式 */ private HSSFCellStyle getRowDataStyle() { HSSFCellStyle style = workbook.createCellStyle(); // 設置這些樣式 style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 生成另外一個字體 HSSFFont font = workbook.createFont(); font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL); style.setFont(font); return style; } }
那麼接下來咱們要如何重構呢?咱們從問題的角度來重構吧正則表達式
1. 在上面代碼中咱們能夠看到有兩個建立樣式的函數,這兩個函數getRowTitleStyle()和getRowDataStyle()能夠說是導出execl的默認樣式。在這裏咱們思考下,若是咱們對導出的樣式有變更是否是又要修改ExportExcel類?數據庫
/** * Description:生成數據標題樣式 */ private HSSFCellStyle getRowTitleStyle() { HSSFCellStyle style = workbook.createCellStyle(); // 設置這些樣式 style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 生成一個字體 HSSFFont font = workbook.createFont(); font.setColor(HSSFColor.VIOLET.index); font.setFontHeightInPoints((short) 12); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 把字體應用到當前的樣式 style.setFont(font); return style; } /** * Description:生成數據行樣式 */ private HSSFCellStyle getRowDataStyle() { HSSFCellStyle style = workbook.createCellStyle(); // 設置這些樣式 style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 生成另外一個字體 HSSFFont font = workbook.createFont(); font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL); style.setFont(font); return style; }
2. 接下來咱們看看導出的主方法exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern)。有沒有以爲參數有點多了,能不能將一些參數合併成一個類或者去除沒用的參數?設計模式
3. 咱們再來看看產生數據行函數createRowData的數據處理方式,以下。雖然這裏只有兩個if...else,可是咱們能不能用什麼設計模式將他們提取出來,方便之後增長類型時沒必要修改原有的類?app
if (value instanceof Boolean) { boolean bValue = (Boolean) value; textValue = "男"; if (!bValue) { textValue = "女"; } } else if (value instanceof Date) { Date date = (Date) value; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); textValue = sdf.format(date); } else { // 其它數據類型都看成字符串簡單處理 textValue = value.toString(); }
4. 咱們在思考下第三個問題,數據的轉換不只僅只有上面那麼循序漸進的,就比如一輛車的狀態有正常,損壞、維修中、報廢等等,可是在數據庫中是以0、一、二、3來存儲的,那麼有什麼好的方法能夠在不修改原有代碼下進行轉換呢?ide
好了,這篇博文就圍繞着上面四個問題進行重構,你們能夠試着按照這幾個問題對上面代碼進行重構,相信通過本身思考和動手後會對代碼的設計有進一步的理解。函數
針對上面問題,咱們是否是能夠將樣式建立抽象成類?工具
/** * Description:生成數據標題樣式 */ private HSSFCellStyle getRowTitleStyle() { HSSFCellStyle style = workbook.createCellStyle(); // 設置這些樣式 style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 生成一個字體 HSSFFont font = workbook.createFont(); font.setColor(HSSFColor.VIOLET.index); font.setFontHeightInPoints((short) 12); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 把字體應用到當前的樣式 style.setFont(font); return style; }
從上面代碼能夠置頂HSSFCellStyle的建立須要用到Workbook對象,因此接口就不適用。測試
而後上面代碼中能夠看出一個總體的樣式還包含了字體,那麼就能夠設計一個抽象類,以下字體
public abstract class AbstractCellStyle { HSSFWorkbook workbook; HSSFCellStyle style; HSSFFont font; public AbstractCellStyle(HSSFWorkbook workbook) { this.workbook = workbook; style = workbook.createCellStyle(); font = workbook.createFont(); } public abstract void setStyle(); public abstract void setFont(); public HSSFCellStyle getCellStyle() { style.setFont(font); return style; } }
該抽象類AbstractCellStyle有一個有參構造函數、三個成員變量、一個得到樣式的方法和兩個抽象方法setStyle和setFont。
子類經過繼承AbstractCellStyle,而後重寫方法setStyle和setFont便可。
構造函數須要傳入Workbook,而後在構造函數裏建立樣式和字體對象,最後調用getCellStyle方法得到HSSFCellStyle便可。
我這裏弄了兩個默認的樣式了,以下
默認數據行樣式類
DefaultDataCellStyle.java
public class DefaultDataCellStyle extends AbstractCellStyle{ public DefaultDataCellStyle(HSSFWorkbook workbook) { super(workbook); } @Override public void setStyle() { style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); } @Override public void setFont() { font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL); } }
默認一個數據標題樣式,
DefaultTitleCellStyle.java
public class DefaultTitleCellStyle extends AbstractCellStyle{ public DefaultTitleCellStyle(HSSFWorkbook workbook) { super(workbook); } @Override public void setStyle() { style.setFillForegroundColor(HSSFColor.SKY_BLUE.index); style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); style.setBorderBottom(HSSFCellStyle.BORDER_THIN); style.setBorderLeft(HSSFCellStyle.BORDER_THIN); style.setBorderRight(HSSFCellStyle.BORDER_THIN); style.setBorderTop(HSSFCellStyle.BORDER_THIN); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); } @Override public void setFont() { font.setColor(HSSFColor.VIOLET.index); font.setFontHeightInPoints((short) 12); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); } }
而後ExportExcel這個類也須要作出相應的修改,修改內容以下
1. 刪除兩個函數getRowTitleStyle()和getRowDataStyle()
2. 增長兩個成員變量,且將Workbook也抽象成成員變量
private HSSFWorkbook workbook; private AbstractCellStyle titleCellStyle;//標題行樣式 private AbstractCellStyle dataCellStyle;//數據行樣式
3. 修改構造函數,以下
public ExportExcel() { this(new HSSFWorkbook()); } /** * 這裏能夠定義兩個常量,可是這裏須要workbook,因此就沒有抽取出來 * @param workbook */ public ExportExcel(HSSFWorkbook workbook) { this(workbook,new DefaultTitleCellStyle(workbook),new DefaultDataCellStyle(workbook)); } public ExportExcel(HSSFWorkbook workbook, AbstractCellStyle titleCellStyle, AbstractCellStyle dataCellStyle) { this.workbook = workbook; this.titleCellStyle = titleCellStyle; this.dataCellStyle = dataCellStyle; }
2. 其餘調用getRowTitleStyle()和getRowDataStyle()方法的修改爲調用成員變量的方法。
那麼如今咱們再來看看這個問題,若是咱們對導出的樣式有變更是否是又要修改ExportExcel類?
如今咱們須要修改導出的標題樣式或者數據行樣式的話,咱們只須要寫個類繼承AbstractCellStyle,而後設置字體和樣式便可,是否是很方便。若是你有更好的建議在評論處留言哦
原來的以下
public void exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern) {...}
每一個工做簿(sheet)都有本身的表格,每一個表格都有本身的行和列
在咱們處處這裏是以每一行對應數據庫中一張表的一條記錄,每一列就比如數據庫中的一張表的一個屬性。
如今咱們現將每一列抽象成一個類,每一列中包含一個標題和數據所對應的實體屬性,例如,學號是標題,學號在實體Student中對應的屬性是id
抽象出來的類以下
public class CellEntity { private String title; private String filedName; //set get方法... }
接下來咱們把每一個工做簿(sheet)也抽象成一個類,這個類中包含多個列(CellEntity)、工做簿名稱(sheetName)、數據集合。
public class SheetEntity { private String sheetName; private List<CellEntity> cellEntitys; private Collection dataset; //set get方法 }
最後修改導出的主方法exportExcel和其餘一些參數的獲取
public void exportExcel(SheetEntity sheetEntity, OutputStream out) {...}
String textValue = null; if (value instanceof Boolean) { boolean bValue = (Boolean) value; textValue = "男"; if (!bValue) { textValue = "女"; } } else if (value instanceof Date) { Date date = (Date) value; SimpleDateFormat sdf = new SimpleDateFormat(pattern); textValue = sdf.format(date); } else { // 其它數據類型都看成字符串簡單處理 textValue = value.toString(); }
從上面代碼中能夠看出value被判斷是哪一種類型,而後再進行賦值操做,顯示被判斷是不是Boolean,而後判斷Date,最後都被當成字符串處理。
這裏博主採用的是以下方法,將數據轉換抽象成一個抽象類,以下
public abstract class AbstractDataHandler { private AbstractDataHandler abstractDataHandler; public AbstractDataHandler(AbstractDataHandler abstractDataHandler) { this.abstractDataHandler = abstractDataHandler; } public abstract String dataHandle(Object value); protected String nextHandle(Object value) { if (abstractDataHandler != null) { return abstractDataHandler.dataHandle(value); } return null; } }
子類經過集成該抽象類,實現dataHandle方法,若是是當前類型,則處理後返回,不然調用抽象類AbstractDataHandler的nextHandle方法繼續調用下個數據處理方法。如今來看看我寫的幾個數據處理類
Boolean數據處理
public class BooleanDataHandler extends AbstractDataHandler { public BooleanDataHandler(AbstractDataHandler abstractDataHandler) { super(abstractDataHandler); } @Override public String dataHandle(Object value) { if (value instanceof Boolean) { boolean bValue = (Boolean) value; String textValue = "是"; if (!bValue) { textValue = "否"; } return textValue; } else { return nextHandle(value); } } }
Date數據處理
public class DateDataHandler extends AbstractDataHandler { public DateDataHandler(AbstractDataHandler abstractDataHandler) { super(abstractDataHandler); } @Override public String dataHandle(Object value) { if (value instanceof Date) { Date date = (Date) value; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } else { return nextHandle(value); } } }
String數據處理
public class StringDataHandler extends AbstractDataHandler { public StringDataHandler(AbstractDataHandler abstractDataHandler) { super(abstractDataHandler); } @Override public String dataHandle(Object value) { return value.toString(); } }
而後建立一個工廠類DataHandlerFactory,工具類調用這個工廠類的dataHandle方法,數據處理就會按照Boolean-->Date-->String流程走下去,都沒有處理就返回null
public class DataHandlerFactory { private static AbstractDataHandler dataHandler = new BooleanDataHandler( new DateDataHandler( new StringDataHandler(null))); public static String dataHandle(Object value) { return dataHandler.dataHandle(value); } }
修改後的代碼以下,是否是簡便了許多
-------------->
如今若是須要增長默認的類型處理,只須要增長AbstractDataHandler的子類,而後修改DataHandlerFactory工廠類便可。
關於這個問題,咱們須要增長一個轉換接口,而後將數據轉換交給子類來實現便可。其實在實體類CellEntity增長一個類型轉換的成員變量便可。接口以下
public interface DataConversion { String transferData(Object data); }
實體類CellEntity也須要作相應的修改,增長一個成員變量便可
public class CellEntity { private String title; private String fieldName; private DataConversion conversion; ...... }
而後處處工具類中也要作修改,由原先交給工廠處理的修改成判斷cellEntity對象的成員變量conversion是否爲null,爲null則交給默認的數據處理類處理,不爲null則交給conversion處理
if (cellEntity.getConversion() == null) textValue = DataHandlerFactory.dataHandle(value); else textValue = cellEntity.getConversion().transferData(value);
就按上面車輛狀態來舉個相應的例子吧,判斷傳入的類型是否爲0或者1,0返回正常,1返回異常,其餘的都返回null,以下
public class CarStatusExportConversion implements DataExportConversion { @Override public String transferData(Object data) { if (data == null) return null; Integer carStatus = (Integer) data; switch (carStatus){ case 0: return "正常"; case 1: return "異常"; } return null; } }
ExportExecl工具類
public class ExportExcel<T> { private HSSFWorkbook workbook; private AbstractCellStyle titleCellStyle;//標題行樣式 private AbstractCellStyle dataCellStyle;//數據行樣式 public ExportExcel() { this(new HSSFWorkbook()); } /** * 這裏能夠定義兩個常量,可是這裏須要workbook,因此就沒有抽取出來 * @param workbook */ public ExportExcel(HSSFWorkbook workbook) { this(workbook,new DefaultTitleCellStyle(workbook),new DefaultDataCellStyle(workbook)); } public ExportExcel(HSSFWorkbook workbook, AbstractCellStyle titleCellStyle, AbstractCellStyle dataCellStyle) { this.workbook = workbook; this.titleCellStyle = titleCellStyle; this.dataCellStyle = dataCellStyle; } public void exportExcel(SheetEntity sheetEntity, OutputStream out) { // 生成一個表格 HSSFSheet sheet = workbook.createSheet(sheetEntity.getSheetName()); // 生成數據標題和數據行樣式 HSSFCellStyle rowTirtleStyle = titleCellStyle.getCellStyle(); HSSFCellStyle rowDataStyle = dataCellStyle.getCellStyle(); //建立數據標題和數據行 createRowTitle(sheetEntity.getCellEntitys(), sheet, rowTirtleStyle); createRowData(sheetEntity.getCellEntitys(),sheetEntity.getDataset(), sheet, rowDataStyle); //寫入流 writeExecl(out); } /** * Description:寫入到OutputStream */ private void writeExecl(OutputStream out) { try { workbook.write(out); } catch (IOException e) { e.printStackTrace(); } } /** * Description: 產生數據行 */ private void createRowData(List<CellEntity> cellEntitys, Collection<T> dataset, HSSFSheet sheet, HSSFCellStyle rowDataStyle) { // 遍歷集合數據,產生數據行 Iterator<T> it = dataset.iterator(); int index = 0; while (it.hasNext()) { index++; HSSFRow row = sheet.createRow(index); T t = (T) it.next(); for (int i = 0; i < cellEntitys.size(); i++) { HSSFCell cell = row.createCell(i); cell.setCellStyle(rowDataStyle); try { String textValue = null; CellEntity cellEntity = cellEntitys.get(i); Object value = PropertyUtils.getProperty(t, cellEntity.getFiledName()); if (cellEntity.getConversion() == null) textValue = DataHandlerFactory.dataHandle(value); else textValue = cellEntity.getConversion().transferData(value); cell.setCellValue(textValue); } catch (Exception e) { e.printStackTrace(); } } } } /** * Description: 產生表格標題行 */ private void createRowTitle(List<CellEntity> cellEntitys, HSSFSheet sheet, HSSFCellStyle rowTirtleStyle) { HSSFRow row = sheet.createRow(0); for (int i = 0; i < cellEntitys.size(); i++) { HSSFCell cell = row.createCell(i); cell.setCellStyle(rowTirtleStyle); HSSFRichTextString text = new HSSFRichTextString(cellEntitys.get(i).getTitle()); cell.setCellValue(text); } } }
Test導出測試類
public class Test { public static void main(String[] args) { // 測試學生 ExportExcel<Student> ex = new ExportExcel<Student>(); // 測試圖書 ExportExcel<Book> ex2 = new ExportExcel<Book>(); List<Student> studentList = getStudentList(); try { OutputStream out = new FileOutputStream("E://a.xls"); ex.exportExcel(getStudentSheetEntity("學生",studentList), out); out.close(); System.out.println("excel導出成功!"); } catch (Exception e) { e.printStackTrace(); } } private static SheetEntity getStudentSheetEntity(String sheetName,List<Student> studentList){ List<CellEntity> cellEntitys = new ArrayList<CellEntity>(); cellEntitys.add(new CellEntity("學號","id")); cellEntitys.add(new CellEntity("姓名","name")); cellEntitys.add(new CellEntity("年齡","age")); cellEntitys.add(new CellEntity("性別","sex", new SexDataConversion())); cellEntitys.add(new CellEntity("出生日期","birthday")); SheetEntity entity = new SheetEntity(sheetName,cellEntitys , studentList); return entity; } private static List<Student> getStudentList() { List<Student> datas = new ArrayList<Student>(); datas.add(new Student(10000001, "張三", 20, true, new Date())); datas.add(new Student(20000002, "李四", 24, false, new Date())); datas.add(new Student(30000003, "王五", 22, true, new Date())); return datas; } } class SexDataConversion implements DataConversion{ @Override public String transferData(Object data) { if (data instanceof Boolean) { boolean bValue = (Boolean) data; String textValue = "男"; if (!bValue) { textValue = "女"; } return textValue; } return null; } }