在查询接口结果类设计中,有这么一种思路,即把查询的真实结果和结果码组合起来,形成一个结果类,当调用方使用该接口时,先判断结果是否是成功结果,然后进行相应的处理。
一个示例如下:
该实现的优点是简单。当需要构建一个失败结果的时候,调用单参数构造方法,当需要构建一个成功结果的时候,调用双参数构造方法。
该实现的缺点如下:
该类没有主动保持自己的不变量,完全依赖调用方对该类不变量的理解。从该类的设计思路,可以很快的推出,该类应该满足以下性质:
任何时刻,该类的结果码不为null。
当结果码为ResultCode.SUCCESS时,该结果为成功结果,调用方可以使用真实的结果对象resultObject。
当结果码不为ResultCode.SUCCESS时,该结果为失败结果,resultCode表明了失败的原因,结果对象resultObject无定义。
默认构造函数导致resultCode为null,单参数构造函数有可能误传null或者成功码,双参数构造函数有可能误传null或者失败码。
总而言之,该对象的不变量的保持,必须依赖调用方的正确使用。
在该类的实际使用中,遵循的标准是失败结果调用单参数构造函数,成功结果调用双参数构造函数,但是由于构造函数和类名同名,该语义没有清晰的表达出来。
因为所有的成功结果都是调用双参数构造函数,因此,在正确使用该类的情况下,传入的resultCode都是ResultCode.SUCCESS,这个实际上是一种重复和冗余的体现。
基于以上的分析,代码重构如下:
resultCode赋予一个默认值ResultCode.UNKNOWN_EXCEPTION,保证了即使使用默认构造函数,创建的对象也是一个合法的对象。
使用静态方法代替构造函数来构建对象,由于静态方法可以自定义方法名,可以明确的指明方法的使用场景。
在静态方法中,使用防御性编程,保持对象的不变量。该处也可以更为强硬的使用异常来指明参数错误。
回收对成功结果结果码的赋值,去除了调用方的对成功结果码的冗余重复使用。
Setter和Getter方法为系统框架使用,为了保持简单,没有加上防御性代码。
代码比重构前稍微复杂一点,但是考虑到该实现可以较好的保持对象的不变量,明确了方法的调用场景,以及去除了冗余代码,个人认为这样的实现优于原有实现。
一个示例如下:
/**
* 列表查询结果。
* <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方法为系统框架使用,为了保持简单,没有加上防御性代码。
代码比重构前稍微复杂一点,但是考虑到该实现可以较好的保持对象的不变量,明确了方法的调用场景,以及去除了冗余代码,个人认为这样的实现优于原有实现。