从零开始—我的开源项目6:代码完善(下)

前面提到了打包的操作,自然不得不说一下maven了。

可以将maven理解为管理jar包的工具:既可以管理依赖的jar包,也可以简化打包的操作。

关于maven的介绍,网上资料很多,这里只在用到时,简单介绍命令。

maven的安装配置,以及Eclipse的m2e插件安装,请自行查找相关资料。


命令行创建maven项目,请参考官方提供的 maven in 5 minutes(http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html

安装过m2e插件的eclipse可执行通过new--》other-->maven project创建


为了简化操作,maven提供了多种模板可供选择,这将简化了基础包的创建,以及一些依赖包的引入。

这里选择最基础的 maven-archetype-quickstart。

首先是依赖包的导入,在pom.xml配置相关的依赖包

	<dependencies>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.10-FINAL</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-scratchpad</artifactId>
			<version>3.10-FINAL</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
scope代表的执行范围,test指只在测试阶段运行,junit不会被打入到正式的jar包中。

相关的jar包,你可以根据名字在maven仓库中搜索。

maven仓库有:http://search.maven.org 和 http://mvnrepository.com/


maven只是工具,辅助开发之用,核心还是代码。

下面就来谈谈代码。之前的代码,虽然实现了功能,但我一直有一个疑问:这是面向对象吗?

相信不少人,学习java的第一课就被告知:万物皆对象。

但是之前的代码,几乎都是static方法,纯粹的面向过程。想事物抽象成java对象,也许很简单,但我觉得这个思考过程是苦恼的。

做了很多尝试,结果都是舍本逐末,为了面向对象而面向对象。

思考很多,得出自认为的结论:工具类和对象的区别在于是否需要共享的数据属性。 如果有private 和set、get的属性,则需要对象;否则,只为了某一功能,和其他方法没有交互,又需要大量被其他类调用,则可采用static方法的工具类。

直接动手写代码,确实不是一个好习惯,必须在一开始想好整体的结构。停止写代码,苦思冥想后,试图切换一个切入点:

以前是从ExcelUtil类开始想起,然后是WordUtil。

现在,我在思考,

首先,如果我只暴露给别人一个方法,用来实现这一功能,我需要哪些东西。

答案:定义的接口是需要word文件和excel文件的路径,已经生成文件的目录。autoExport(String wordPath, String excelPath, String exportDir) 

其次,获得了这些参数,怎么处理他们呢?

答案:怎么处理这个文件,只有他们本身最清楚。于是,我需要两个对象,一个负责读取excel,另一个负责读取word模板,替换并生成新的word文件。而具体怎么处理,则应该是对象本身的事,和接口无关。

再次,两个对象之前有些相同的属性,如文件路径,文件名,类型等。是不是可以将相同属性放置到父类对象中?

答案:必须的。

最后,如果暴露的接口是工具类,static方法,那么怎么调用对象的方法呢?

答案:可以将工具类作为static对象属性放入接口类中,并在调用autoExport时,先new这两个工具类。

本来就是很抽象的事,只是这么说就更抽象了,所以我们还是来看代码:

首先是OfficeFileType类,增加了getLabe()l和parse()方法,分别用来区分word文件还是excel文件和根据后缀得到type枚举

package com.crick.excel2word.core.enumeration;

public enum OfficeFileType {
	
	WORD_03_TYPE(".doc"),
	WORD_07UP_TYPE(".docx"),
	EXCEL_03_TYPE(".xls"),
	EXCEL_07UP_TYPE(".xlsx")
	;
	private OfficeFileType(String suffix) {
		this.suffix = suffix;
	}
	
	private String suffix;
	
	public String getSuffix() {
		return suffix;
	}
	
	public String getLabel() {
		switch (this) {
			case EXCEL_03_TYPE:
			case EXCEL_07UP_TYPE:
				return "excel";
			case WORD_03_TYPE:
			case WORD_07UP_TYPE:
				return "word";
			default: 
					return "";
		}
	}
	
	public static OfficeFileType parse(String suffix) {
		OfficeFileType[] values = OfficeFileType.values();
		for (OfficeFileType type : values) {
			if(suffix.equals(type.getSuffix())) {
				return type;
			}
		}
		return null;
	}
}

然后是两个工具对象的父对象,提供了一些共有属性,并通过构造函数初始化这些数值,所以属性之提供了get方法。

并且提供了一个scanDir()方法,可通过搜索得到某一目录下的某种类型文件的集合。

package com.crick.excel2word.util;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import com.crick.excel2word.core.enumeration.OfficeFileType;

public class OfficeFileUtil {
	
	private String fullPath;
	private String dirPath;
	private String fileName;
	private String fileSuffix;
	private File file;
	private OfficeFileType officeFileType;
	
	protected OfficeFileUtil(String fullPath) {
		file = new File(fullPath);
		if(file.exists()) {
			this.fullPath = fullPath;
			this.dirPath = fullPath.substring(0, fullPath.lastIndexOf(File.separator));
			this.fileName = fullPath.substring(fullPath.lastIndexOf(File.separator)+1, fullPath.length());
			this.fileSuffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
			this.officeFileType = OfficeFileType.parse(this.fileSuffix);
		}
	}
	
	public String getDirPath() {
		return dirPath;
	}
	public String getFileName() {
		return fileName;
	}
	public String getFullPath() {
		return fullPath;
	}
	public File getFile() {
		return file;
	}
	public String getSuffix() {
		return fileSuffix;
	}
	public OfficeFileType getOfficeFileType() {
		return officeFileType;
	}
	
	/**
	 * 扫描文件下某类型的文件
	 * 
	 * @param dirPath 目录路径
	 * @param types office文件类型
	 * @return 
	 */
	protected List<File> scanDir(String dirPath, OfficeFileType... types) {
		List<File> files = null;
		File dir = new File(dirPath);
		if (dir.exists() && dir.isDirectory() && types.length >0){
			files = new ArrayList<File>();
			for (File file : dir.listFiles()) {
				if (!file.isDirectory() && isEndsWith(file.getName(), types)) {
					files.add(file);
				}
			}
		}
		return files;
	}
	
	private boolean isEndsWith(String fileName, OfficeFileType[] types) {
		String suffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
		for (int i=0; i<types.length; i++) {
			if (suffix.equals(types[i].getSuffix())){
				return true;
			}
		}
		return false;
	}
}

WordUtil对象,去掉了static方法,并且和直接获取父类对象中的属性,所以减少了方法参数。

并提供了scanDirWord方法,可搜索某一目录下的word文件(包括03和07以后)

package com.crick.excel2word.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;

import com.crick.excel2word.core.enumeration.OfficeFileType;

public class WordUtil extends OfficeFileUtil {
	
	public WordUtil(String fullPath) {
		super(fullPath);
	}

	public List<File> scanDirWord(String dirPath) {
		return super.scanDir(dirPath, OfficeFileType.WORD_03_TYPE, OfficeFileType.WORD_07UP_TYPE);
	}

	public void export(Map<String, Object> params, String exportDir, String exportName) {
		FileOutputStream fos = null;
		if(exportName == null) {
			exportName = "未命名";
		}
		try {
			HWPFDocument doc = new HWPFDocument(new FileInputStream(getFile()));
			Range range = doc.getRange();
			for (String key : params.keySet()) {
				range.replaceText(key, params.get(key).toString());
			}
			fos = new FileOutputStream(getRenameFile(exportDir, exportName, getOfficeFileType()));
			doc.write(fos);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					fos = null;
				}
			}
		}
	}
	private File getRenameFile(String path, String docName, OfficeFileType type) {
		//解决重名问题
		int i = 1;
		File doc = new File(path + docName+type.getSuffix());
		while (doc.exists()) {
			doc = new File(path + docName+"("+i+")"+type.getSuffix());
			i++;
		}
		return doc;
	} 
	
}

ExcelUtil对象,好吧,同上

package com.crick.excel2word.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.crick.excel2word.core.enumeration.OfficeFileType;

public class ExcelUtil extends OfficeFileUtil {

	public ExcelUtil(String fullPath) {
		super(fullPath);
	}

	public List<File> scanDirExcel(String dirPath) {
		return super.scanDir(dirPath, OfficeFileType.EXCEL_03_TYPE, OfficeFileType.EXCEL_07UP_TYPE);
	}
	
	public List<Map<String, Object>> extract() {
		List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
		Workbook book;
		try {
			book = new HSSFWorkbook(new BufferedInputStream(new FileInputStream(getFile())));
			Sheet sheet = book.getSheetAt(0);
			if (sheet.getLastRowNum() == 0) {
				return null;
			}
			List<String> keys = getKeyList(sheet.getRow(0));
			for (int i=sheet.getFirstRowNum()+1; i <= sheet.getLastRowNum(); i++) {
				Map<String, Object> param = extractRow(sheet.getRow(i), keys);
				results.add(param);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return results;
	}
	
	private Map<String, Object> extractRow(Row row, List<String> keys){
		Map<String, Object> params = new HashMap<String, Object>();
		for (int i=0; i<row.getLastCellNum(); i++) {
			if (keys.get(i) == null || keys.get(i) == "") {
				continue;
			}
			row.getCell(i).setCellType(Cell.CELL_TYPE_STRING);
			params.put("#{"+keys.get(i)+"}", row.getCell(i));
		}
		return params; 
	}
	
	/**
	 * 第一行作为key,即mark标签名
	 * 
	 * @param row
	 * @return
	 */
	private List<String> getKeyList(Row row) {
		List<String> keys = new ArrayList<String>();
		for (int i=0; i<=row.getLastCellNum();i++) {
			if (row.getCell(i) != null) {
				row.getCell(i).setCellType(Cell.CELL_TYPE_STRING);
			} 
			keys.add(row.getCell(i)==null?"":row.getCell(i).toString().toLowerCase());
		}
		//如果未指定name标签,则默认第一列为生成的word文件名
		if (!keys.contains("name")) {
			keys.set(0, "name");
		}
		return keys;
	}
}

最后是暴露出去的工具类,也就是我们定义的接口Excel2Words

package com.crick.excel2word;

import java.io.File;
import java.util.List;
import java.util.Map;

import com.crick.excel2word.util.ExcelUtil;
import com.crick.excel2word.util.WordUtil;

public class Excel2Words {

	private static WordUtil wordUtil;
	private static ExcelUtil excelUtil;
	
	private static void init(String wordPath, String excelPath) {
		wordUtil = new WordUtil(wordPath);
		excelUtil = new ExcelUtil(excelPath);
	}
	

	public static void autoExport(String wordPath, String excelPath, String exportDir) {
		init(wordPath, excelPath);
		if (!exportDir.endsWith(File.separator)) {
			exportDir = exportDir.concat(File.separator);
		}
		List<Map<String, Object>> rowList = excelUtil.extract();
		for (Map<String, Object> rowParam : rowList) {
			wordUtil.export(rowParam, exportDir, rowParam.get("#{name}").toString());
		}
	}
}

至此,代码阶段完成。

我不确定这就是面向对象的思考方式和变成方法,但至少我在改变着自己编程思维。

最大的收获是,知道了思考的作用远大于写代码本身。

最多的bug是关于目录的"/",这个问题出现了多次,在代码中都增加了相关控制。

关于String是否为空的控制,并没有过多的判断。我觉得这些基础的判断应该在上次,调用者进行判断。

maven给项目打包,可以通过命令行,在pom所在文件夹使用命令

mvn clean install
生成的jar包在target目录下。

maven有一个有趣的机制是,你可以在一行中输入多个命令,maven会依次执行。

上面的命令,其实是mvn clean 清除target目录下的内容, mvn install 打包。

其实maven打包的命令是mvn package,同样也会在打包的同时进行编译和测试,但是install多了一项功能,就是想jar包发布到本地maven仓库中,因此mvn clean install更加常用。
如果将excel2word-0.0.3-SNAPSHOT.jar放入其他项目中使用,会发现无法运行,因为这个项目可能没有引入依赖的poi等jar包。

而如果将依赖包也打入excel2word-0.0.3-SNAPSHOT.jar中,则有可能因为其他项目也引入的相同包,而造成冲突。面对这种问题,暂时我还不知道好的解决方法,但是我还是提供一下将依赖包打入jar的方法,可根据具体情形分析使用。

利用maven的assembly插件。现在pom增加相关plugin的配置,然后执行命令

mvn clean install assembly:assembly

附完整pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.crick</groupId>
	<artifactId>excel2word</artifactId>
	<version>0.0.3-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>excel2word</name>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.10-FINAL</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-scratchpad</artifactId>
			<version>3.10-FINAL</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值