实践中的重构31_结果类两种实现的比较

在查询接口结果类设计中,有这么一种思路,即把查询的真实结果和结果码组合起来,形成一个结果类,当调用方使用该接口时,先判断结果是否是成功结果,然后进行相应的处理。
一个示例如下:
/**
 * 列表查询结果。
 * <p>
 * 请在处理成功时[ isSuccess() == true; ]才使用查询结果对象。
 * </p>
 * 
 */
public class QueryListResult {

	private List<Book> resultObject;

	private ResultCode resultCode;

	public QueryListResult() {
	}

	public QueryListResult(ResultCode resultCode) {
		this.resultCode = resultCode;
		this.resultObject = null;
	}

	public QueryListResult(ResultCode resultCode, List<Book> bookList) {
		this.resultCode = resultCode;
		this.resultObject = bookList;
	}

	public boolean isSuccess() {
		return ResultCode.SUCCESS == resultCode;
	}

	public List<Book> getResultObject() {
		return resultObject;
	}

	public void setResultObject(List<Book> bookList) {
		this.resultObject = bookList;
	}

	public ResultCode getResultCode() {
		return resultCode;
	}

	public void setResultCode(ResultCode resultCode) {
		this.resultCode = resultCode;
	}
}

该实现的优点是简单。当需要构建一个失败结果的时候,调用单参数构造方法,当需要构建一个成功结果的时候,调用双参数构造方法。
该实现的缺点如下:
该类没有主动保持自己的不变量,完全依赖调用方对该类不变量的理解。从该类的设计思路,可以很快的推出,该类应该满足以下性质:
任何时刻,该类的结果码不为null。
当结果码为ResultCode.SUCCESS时,该结果为成功结果,调用方可以使用真实的结果对象resultObject。
当结果码不为ResultCode.SUCCESS时,该结果为失败结果,resultCode表明了失败的原因,结果对象resultObject无定义。
默认构造函数导致resultCode为null,单参数构造函数有可能误传null或者成功码,双参数构造函数有可能误传null或者失败码。
总而言之,该对象的不变量的保持,必须依赖调用方的正确使用。
在该类的实际使用中,遵循的标准是失败结果调用单参数构造函数,成功结果调用双参数构造函数,但是由于构造函数和类名同名,该语义没有清晰的表达出来。
因为所有的成功结果都是调用双参数构造函数,因此,在正确使用该类的情况下,传入的resultCode都是ResultCode.SUCCESS,这个实际上是一种重复和冗余的体现。
基于以上的分析,代码重构如下:
/**
 * 列表查询结果。
 * <p>
 * 请在处理成功时[ isSuccess() == true; ]才使用查询结果对象。
 * </p>
 * 
 */
public class QueryListResult {

	private List<Book> resultObject;

	private ResultCode resultCode = ResultCode.UNKNOWN_EXCEPTION;

	public QueryListResult() {
	}

	public static QueryListResult createFailedResult(ResultCode resultCode) {
		QueryListResult failedResult = new QueryListResult();
		if (resultCode != ResultCode.SUCCESS && resultCode != null) {
			failedResult.resultCode = resultCode;
		}
		return failedResult;
	}

	public static QueryListResult createSuccessfulResult(List<Book> bookList) {
		QueryListResult successfulResult = new QueryListResult();
		successfulResult.resultCode = ResultCode.SUCCESS;
		if (bookList == null) {
			successfulResult.resultObject = new ArrayList<Book>();
		} else {
			successfulResult.resultObject = bookList;
		}
		return successfulResult;
	}

	public boolean isSuccess() {
		return ResultCode.SUCCESS == resultCode;
	}

	public List<Book> getResultObject() {
		return resultObject;
	}

	public void setResultObject(List<Book> bookList) {
		this.resultObject = bookList;
	}

	public ResultCode getResultCode() {
		return resultCode;
	}

	public void setResultCode(ResultCode resultCode) {
		this.resultCode = resultCode;
	}
}

resultCode赋予一个默认值ResultCode.UNKNOWN_EXCEPTION,保证了即使使用默认构造函数,创建的对象也是一个合法的对象。
使用静态方法代替构造函数来构建对象,由于静态方法可以自定义方法名,可以明确的指明方法的使用场景。
在静态方法中,使用防御性编程,保持对象的不变量。该处也可以更为强硬的使用异常来指明参数错误。
回收对成功结果结果码的赋值,去除了调用方的对成功结果码的冗余重复使用。
Setter和Getter方法为系统框架使用,为了保持简单,没有加上防御性代码。
代码比重构前稍微复杂一点,但是考虑到该实现可以较好的保持对象的不变量,明确了方法的调用场景,以及去除了冗余代码,个人认为这样的实现优于原有实现。

猜你喜欢

转载自zhang-xzhi-xjtu.iteye.com/blog/1171155
今日推荐