咱们先来看代码,有点长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; } }