重构

开头先抛出几个问题吧,这几个问题也是《重构:改善既有代码的设计》这本书第2章的问题。

什么是重构?

为什么要重构?

什么时候要重构?

接下来就从这几个问题出发,通过这几个问题来系统的了解重构的意义。

什么是重构?

《重构:改善既有代码的设计》这本书中将重构以名词和动词形式进行解释,如下

重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构(动词):使用一系列重构的手法,在不改变软件可观察行为的前提下,调整其结构。

然后作者又将定义往两个方面扩展

  1. 重构的目的是使软件更容易被理解和修改。
  2. 重构不会改变软件可观察的行为------重构之后软件功能一如既往。

简单来说就是

  1. 对编码人员来说重构可以让代码可读性变高,可扩展性变高,修改变得更容易。
  2. 对用户来说重构不会影响原有的操作流程。

为什么要重构?

  1. 重构改进软件设计
  2. 重构使软件更容易理解
  3. 重构帮忙找到bug
  4. 重构提高编程速度

重构可以让代码变得结构化,利用设计模式对其合理的进行修改,使得代码可读性变高。如果代码没有进行重构,某些功能会变得越来越臃肿,将慢慢失去自己所应有的结构,而后续的人也会在上面添砖加瓦,使得程序越来越难以理解其含义。

什么时候要重构?

关于何时重构,这本书中给出了几点

  1. 事不过三,三则重构(当重复代码超过三次,就应该重构)
  2. 添加功能时重构(代码的设计无法帮助我轻松添加我所需要的特性)
  3. 修补错误时重构(可读性不高,不能清晰快速的理解它)
  4. 复审代码时重构(可以改善开发状态,帮助理解软件系统中的更多部分)

实例

这篇博文主要讲述博主将网上原有的execl导出功能代码进行一步步的重构,设计成一个通用的、可扩展的代码。

我们先来看看早期的代码,如下

public class ExportExcel<T> {
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);
} /**
* 这是一个通用的方法,利用了JAVA的反射机制,可以将放置在JAVA集合中并且符号一定条件的数据以EXCEL 的形式输出到指定IO设备上
*
* @param title
* 表格标题名
* @param headers
* 表格属性列名数组
* @param dataset
* 需要显示的数据集合,集合中一定要放置符合javabean风格的类的对象。此方法支持的
* javabean属性的数据类型有基本数据类型及String,Date,byte[](图片数据)
* @param out
* 与输出设备关联的流对象,可以将EXCEL文档导出到本地文件或者网络中
* @param pattern
* 如果有时间数据,设定输出格式。默认为"yyy-MM-dd"
*/
@SuppressWarnings("unchecked")
public void exportExcel(String title, String[] headers, Collection<T> dataset, OutputStream out, String pattern) {
// 声明一个工作薄
HSSFWorkbook workbook = new HSSFWorkbook();
// 生成一个表格
HSSFSheet sheet = workbook.createSheet(title);
// 设置表格默认列宽度为15个字节
sheet.setDefaultColumnWidth((short) 15);
// 生成一个样式
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);
// 生成并设置另一个样式
HSSFCellStyle style2 = workbook.createCellStyle();
style2.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
style2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
style2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style2.setBorderRight(HSSFCellStyle.BORDER_THIN);
style2.setBorderTop(HSSFCellStyle.BORDER_THIN);
style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);
style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
// 生成另一个字体
HSSFFont font2 = workbook.createFont();
font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
// 把字体应用到当前的样式
style2.setFont(font2); // 声明一个画图的顶级管理器
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
// 定义注释的大小和位置,详见文档
HSSFComment comment = patriarch.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short) 4, 2, (short) 6, 5));
// 设置注释内容
comment.setString(new HSSFRichTextString("可以在POI中添加注释!"));
// 设置注释作者,当鼠标移动到单元格上是可以在状态栏中看到该内容.
comment.setAuthor("leno"); // 产生表格标题行
HSSFRow row = sheet.createRow(0);
for (short i = 0; i < headers.length; i++) {
HSSFCell cell = row.createCell(i);
cell.setCellStyle(style);
HSSFRichTextString text = new HSSFRichTextString(headers[i]);
cell.setCellValue(text);
} // 遍历集合数据,产生数据行
Iterator<T> it = dataset.iterator();
int index = 0;
while (it.hasNext()) {
index++;
row = sheet.createRow(index);
T t = (T) it.next();
// 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
Field[] fields = t.getClass().getDeclaredFields();
for (short i = 0; i < fields.length; i++) {
HSSFCell cell = row.createCell(i);
cell.setCellStyle(style2);
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 Integer) {
// int intValue = (Integer) value;
// cell.setCellValue(intValue);
// } else if (value instanceof Float) {
// float fValue = (Float) value;
// textValue = new HSSFRichTextString(
// String.valueOf(fValue));
// cell.setCellValue(textValue);
// } else if (value instanceof Double) {
// double dValue = (Double) value;
// textValue = new HSSFRichTextString(
// String.valueOf(dValue));
// cell.setCellValue(textValue);
// } else if (value instanceof Long) {
// long longValue = (Long) value;
// cell.setCellValue(longValue);
// }
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 if (value instanceof byte[]) {
// 有图片时,设置行高为60px;
row.setHeightInPoints(60);
// 设置图片所在列宽度为80px,注意这里单位的一个换算
sheet.setColumnWidth(i, (short) (35.7 * 80));
// sheet.autoSizeColumn(i);
byte[] bsValue = (byte[]) value;
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 6, index, (short) 6,
index);
anchor.setAnchorType(2);
patriarch.createPicture(anchor, workbook.addPicture(bsValue, HSSFWorkbook.PICTURE_TYPE_JPEG));
} 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 {
// 清理资源
}
}
}
try {
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
// 测试学生
ExportExcel<Student> ex = new ExportExcel<Student>();
String[] headers = { "学号", "姓名", "年龄", "性别", "出生日期" };
List<Student> dataset = new ArrayList<Student>();
dataset.add(new Student(10000001, "张三", 20, true, new Date()));
dataset.add(new Student(20000002, "李四", 24, false, new Date()));
dataset.add(new Student(30000003, "王五", 22, true, new Date()));
// 测试图书
ExportExcel<Book> ex2 = new ExportExcel<Book>();
String[] headers2 = { "图书编号", "图书名称", "图书作者", "图书价格", "图书ISBN", "图书出版社", "封面图片" };
List<Book> dataset2 = new ArrayList<Book>();
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://USER//Desktop//1.jpg"));
byte[] buf = new byte[bis.available()];
while ((bis.read(buf)) != -1) {
//
}
dataset2.add(new Book(1, "jsp", "leno", 300.33f, "1234567", "清华出版社", buf));
dataset2.add(new Book(2, "java编程思想", "brucl", 300.33f, "1234567", "阳光出版社", buf));
dataset2.add(new Book(3, "DOM艺术", "lenotang", 300.33f, "1234567", "清华出版社", buf));
dataset2.add(new Book(4, "c++经典", "leno", 400.33f, "1234567", "清华出版社", buf));
dataset2.add(new Book(5, "c#入门", "leno", 300.33f, "1234567", "汤春秀出版社", buf)); OutputStream out = new FileOutputStream("E://a.xls");
OutputStream out2 = new FileOutputStream("E://b.xls");
ex.exportExcel(headers, dataset, out);
ex2.exportExcel(headers2, dataset2, out2);
out.close();
out2.close();
JOptionPane.showMessageDialog(null, "导出成功!");
System.out.println("excel导出成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

这边将整个导出功能都放在一个类里面,这样代码修改起来不容易。

接下来我们先来提炼函数(Extract Method)

上述代码中的导出流程大致上可以分为五个步骤,如下

  1. 创建一系列的对象(workbook、sheet...)
  2. 创建样式、字体
  3. 产生表格标题行
  4. 遍历集合数据,产生数据行
  5. 写入io流

那么我们就可以按照这几个流程,将他们封装成方法,一些对象可以修改才类的成员变量

开始重构

1.创建一系列的对象(workbook、sheet...)

之前的代码

// 声明一个工作薄
HSSFWorkbook workbook = new HSSFWorkbook();

将workbook修改成成员变量,在构造函数时初始化它

    private HSSFWorkbook workbook;

    public ExportExcel() {
this(new HSSFWorkbook());
} public ExportExcel(HSSFWorkbook workbook) {
this.workbook = workbook;
}

2.创建样式、字体

之前的代码

        // 生成一个样式
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);
// 生成并设置另一个样式
HSSFCellStyle style2 = workbook.createCellStyle();
style2.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
style2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
style2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style2.setBorderRight(HSSFCellStyle.BORDER_THIN);
style2.setBorderTop(HSSFCellStyle.BORDER_THIN);
style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);
style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
// 生成另一个字体
HSSFFont font2 = workbook.createFont();
font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
// 把字体应用到当前的样式
style2.setFont(font2);

将创建两个样式代码提炼成两个函数,数据标题样式方法getRowTitleStyle和数据行样式方法getRowDataStyle

    /**
* 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;
}

3、4、5.产生表格标题行、遍历集合数据,产生数据行、写入io流

        // 产生表格标题行
HSSFRow row = sheet.createRow(0);
for (short i = 0; i < headers.length; i++) {
HSSFCell cell = row.createCell(i);
cell.setCellStyle(style);
HSSFRichTextString text = new HSSFRichTextString(headers[i]);
cell.setCellValue(text);
}

将上面产生表格标题行代码块提炼成方法,产生数据行和写入io流也是如此。代码就不贴了

提炼后的主方法

    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);
}

提炼后是不是精简了许多,好了,这篇博文暂时就介绍一种重构方式--提炼函数。

后续博文会继续对Java POI 导出EXCEL进行重构

将一些重复代码替换成已有的工具类中的代码

将一些方法提炼成类

将数据封装成对象

等等

觉得不错的可以关注我哦

github地址在这,后续会增加其他各种各样的工具项目,觉得不错的点个star

重构:以Java POI 导出EXCEL为例的更多相关文章

  1. 重构:以Java POI 导出EXCEL为例2

    前言 上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数.这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等. 上一篇博文地址:http://www. ...

  2. Java POI 导出EXCEL经典实现 Java导出Excel

    转自http://blog.csdn.net/evangel_z/article/details/7332535 在web开发中,有一个经典的功能,就是数据的导入导出.特别是数据的导出,在生产管理或者 ...

  3. java:POI导出excel

    POI是一个开源项目,专用于java平台上操作MS OFFICE,企业应用开发中可用它方便导出Excel. 下面是使用示例: 1.maven中先添加依赖项 <dependency> < ...

  4. Java POI 导出excel表

    1.首先下载poi-3.6-20091214.jar,下载地址如下: http://download.csdn.net/detail/evangel_z/3895051 2.Student.java ...

  5. java POI导出Excel文件数据库的数据

    在web开发中,有一个经典的功能,就是数据的导入导出.特别是数据的导出,在生产管理或者财务系统中用的非常普遍,因为这些系统经常要做一些报表打印的工作.这里我简单实现导出Excel文件. POI jar ...

  6. Java POI导出Excel不弹框选择下载路径(下载文件不选择下载路径,默认) Chrome

    在Chrome浏览器中,Java导出Excel文件时,浏览器弹出提示框,需要选择下载路径 在Chrome中的高级设置中,把“下载前询问每个文件的保存位置”去掉就解决了 DEEPLOVE(LC)

  7. JAVA POI导出EXCEL 动态表头、多级表头、动态数据

    导出Excel文件是业务中经常遇到的需求,以下是经常遇到的一些问题: 1,导出中文文件名乱码 String filename = "sheet1";response.setChar ...

  8. java poi导出EXCEL xls文件代码

    String _currentPage = request.getParameter("currentPage"); Integer currentPage = 0; if(_cu ...

  9. Java POI 导出EXCEL经典实现

    import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; i ...

随机推荐

  1. 浅谈java异常

    一. 异常的定义 在<java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看 ...

  2. MySQL 数据库开发的 36 条军规

    MySQL 数据库开发的 36 条军规 写在前面的话: 总是在灾难发生后,才想起容灾的重要性: 总是在吃过亏后,才记得曾经有人提醒过. (一)核心军规 (1)不在数据库做运算:cpu计算务必移至业务层 ...

  3. Android进阶(十)Android 发邮件

    最近在做的APP涉及到发邮件,总结如下: 在android里进行邮件客户端开发可以有两种方式: 在邮件客户端的设计中,可以采用两种方法. 一种是调用android系统自带的邮件服务 优点:这种方法比较 ...

  4. 【Android 应用开发】 ActionBar 基础

    作者 : 万境绝尘 (octopus_truth@163.com) 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/3920439 ...

  5. java组播MulticastSocket

    在单播模式中有服务器端和客户端之分,而组播模式与单播模式不同,每个端都是以路由器或交换机做为中转广播站,任意一端向路由器或交换机发送消息,路由或交换机负责发送其他节点,每个节点都是同等的.所以在编程模 ...

  6. 开源视频平台:ViMP

    ViMP是一个开源的视频平台,可以用于建立自己的视频门户.可以用于VoD系统,网络学习系统,企业内部视频系统的搭建. 这一阵子一直在研究网络视频平台.发现这类的开源系统相对来说还是比较少的,因此在发现 ...

  7. 一个不错的扩展:Ext.ux.container.ButtonSegment

    地址:http://www.sencha.com/forum/showthread.php?132048-Ext.ux.container.ButtonSegment

  8. 命令行界面的C/S聊天室应用 (Socket多线程实现)

    命令行界面即在Eclipe控制台输入数据. 服务器端包含多个线程,每个Socket对应一条线程,该线程负责读取对应输入流的数据(从客户端发送过来的数据),并将读到的数据向每个Socket输出流发送一遍 ...

  9. DB Query Analyzer 6.03, the most excellent Universal DB Access tools on any Microsoft Windows OS

      DB Query Analyzer 6.03, the most excellent Universal database Access tools on any Microsoft Wind ...

  10. Sample Code for Qp_preq_pub.Price_request Api to Simulate an Ask for Promotion Modifier

    DECLARE p_line_tbl QP_PREQ_GRP.LINE_TBL_TYPE; p_qual_tbl QP_PREQ_GRP.QUAL_TBL_TYPE; p_line_attr_tbl ...