设计模式综合运用之Excel导入
学以致用,前几篇文章我们学了很多设计模式,今天我们就把这些模式综合运用一下,看看实际应用是怎么导入Excel的,(当然这里面没有用到侦听者模式)
我们先看一下Excel的样子:
因为是Excel的的解析,所以应该是前台上传的,我们应该有一个Servlet来接收前台传过来的数据:
package cn.bridgeli.demo.servlet; import java.io.File; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.bridgeli.demo.servce.Container; import cn.bridgeli.demo.servce.ExcelParseStrategy; public class ExcelParseServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //真正的情况下应该由前台传过来的,这里是模拟就写死了 File file = new File("D:/Excel.xls"); String excelType = request.getParameter("excelType"); // 此处是策略模式,至于怎么实现,大家可以参考策略模式 Container container = new Container(); ExcelParseStrategy excelParseStrategy = container.getStrategy(excelType); excelParseStrategy.parse(file); } }
下面看ExcelParseStrategy接口,和具体策略的实现:
package cn.bridgeli.demo.servce; import java.io.File; public interface ExcelParseStrategy { void parse(File file); } package cn.bridgeli.demo.servce; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException; public abstract class AbstractExcelParseStrategy implements ExcelParseStrategy { protected static Map<String, String> blocks = new HashMap<String, String>(); @Override public void parse(File file) { Workbook workbook = null; try { Map<String, Object> data = new HashMap<String, Object>(); workbook = Workbook.getWorkbook(file); Sheet[] sheets = workbook.getSheets(); for (Sheet sheet : sheets) { Map<String, Object> sheetData = parseSheet(sheet); data.putAll(sheetData); } storeData(data); } catch (BiffException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != workbook) { workbook.close(); } } } protected Map<String, String> storeData(Map<String, Object> data) { Map<String, String> errors = (Map<String, String>) data.get("error"); if (null != errors && !errors.isEmpty()) { return errors; } storeToDB(data); return null; } protected abstract void storeToDB(Map<String, Object> data); protected Map<String, Object> parseSheet(Sheet sheet) { Map<String, Object> data = new HashMap<String, Object>(); int rsRows = sheet.getRows(); for (int i = 0; i < rsRows; i++) { Cell[] cells = sheet.getRow(i); int cellLength = cells.length; for (int j = 0; j < cellLength; j++) { Cell cell = cells[j]; String cellValue = cell.getContents(); String blockId = blocks.get(cellValue); if (null != blockId && !"".equals(blockId)) { //再次使用策略模式 Container container = new Container(); BlockExcelService blockExcelService = container.getParseStrategy(blockId); i = blockExcelService.execute(i, rsRows, sheet, data); } } } return null; } }
在解析Excel的时候,每一块用的也是一个策略模式,所以下面我们看每一个模块的具体接口和实现,因为很类似,所以我就只写了Profile的实现,其实还有其他模块也是一样的,代码如下:
package cn.bridgeli.demo.servce; import java.util.Map; import jxl.Sheet; public interface BlockExcelService { int execute(int i, int rsRows, Sheet sheet, Map<String, Object> data); } package cn.bridgeli.demo.entity.excel; import java.util.Map; import jxl.Sheet; import cn.bridgeli.demo.entity.User; import cn.bridgeli.demo.servce.BlockExcelService; public class BlockExcelUserProfile implements BlockExcelService { @Override public int execute(int row, int totalRows, Sheet sheet, Map<String, Object> data) { User user = new User(); // 具体在这个Block中,取出每一个cell中的值,并把cell中的值封装到User对象中,具体步骤略。 data.put("user", user); // 在解析的过程中,如果遇到错误,可以把错误信息返回回去 data.put("error", "error message"); // 返回下一个block开始的行,具体情况具体定 return row + 3; } }
在具体解析每一个模块的逻辑里面,要注意几点:
1. 当解析遇到错误的时候,可以把错误信息放到data中,返回,最终返回到前台,告诉用户为什么错了,以修正Excel,重新上传解析;
2. 解析具体的每一行,位置最好相对是相对于传过来的row,这样的话可以做到位置无关性,即不要在代码中写死,这么就做到了:高内聚低耦合!
3. 注意解析每一个模块的结束标志,例如Education 这一模块,我们可以循环解析,但当循环的行数大于所有行数时,就应该结束循环了
解析完成之后,最终保存数据到数据库:
package cn.bridgeli.demo.servce; import java.util.Map; import cn.bridgeli.demo.entity.User; public class UserExcelParseStrategy extends AbstractExcelParseStrategy { public UserExcelParseStrategy() { blocks.put("#Profile", "blockExcelUserProfile"); blocks.put("#Education Experence", "blockExcelUserEducation"); } @Override protected void storeToDB(Map<String, Object> data) { // 取出Data中的每一块,保存到数据库 User user = (User) data.get("user"); // 一下从略 } }
这个事保存解析得到的数据,到数据库,这里只给出了Profile模板块,至于另一个Education模板也是类似的,但我们的Excel中有第三个模块时,只需要具体实现storeToDB()就行了,当然别忘了在blocks中进行注册啦,其中blocks的key就是Excel中每一个模块的名字,value就是对应具体调用每一个解析器中的解析,所以当然和Container类中getParseStrategy()的方法的参数相对应啦
这样就最终实现了我们的Excel的导入,既然是设计模式的综合运用,让我们看看这里面用到的设计模式:
1. 策略模式,相信这个不用说了,代码中有注释,用到了两次;
2. 既然用到了Container,那么这个类,按照我们之前的讲策略模式时,已经说明可以使用工厂模式,代替这个类,即根据实现一个BeanFactory的getBean()方法,然后去读取XML配置文件,产生具体的策略,和Spring的IOC一样;
3. 我们知道无状态的类都可以做成单例的,所以这个不用说了,工厂模式一定可以做成单例的;
4. 模板方法这个不用说了,在把解析出来的每一个模块或这个新增一个模块的数据保存到数据库时这很明显是一个模板方法,我们只需根据具体情况实现storeToDB()这个方法就行了,错误返回已经被我们放到上一层中了,当然解析其实也是模板方法,所以这里面仅仅是没有用到我们之前提到的侦听者模式而已
最后要说明的是:1. 这几个设计模式在我们平时编程中也算是最常用的模式了,所以一定要状掌握吧,要多学多练,2. 这里面贴出来的代码仅仅是示例代码,不排除有很多错误,仅仅是参考,3. 设计模式这个系列目前告一段落,向大家推荐两本讲设计模式的书《大话设计模式》和《Head First设计模式》,这两本书写的绝对经典,没事可以翻翻,注:大家买这两本书,老夫不会从中得到一分钱好处!
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/124
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
近期评论