通过Builder模式和协变返回类型实现基类方法扩展和链式编程
在Java中,使用链式编程可以显著提高编码效率,减少代码数量。实现链式编程的关键是方法返回当前类的引用。此时如果新增一个类,希望该类对已有类的方法进行增强,并且新增的类仍然支持链式编程,此时基类的设计就要满足一定的条件才能满足开闭原则(OCP)。本文将介绍一种简单的方法优雅地实现这一功能。
修改前支持链式编程的基类
在有新的需求前我们并不认为基类需要扩展方法,所以原有基类代码如下:
public abstract class MockBase {
protected MockMvc mockMvc;
protected ResultActions resultActions;
public MockBase perform(RequestBuilder builder) throws Exception {
resultActions = this.mockMvc.perform(builder);
return this;
}
public MockBase okExpect() throws Exception {
resultActions = resultActions.andExpect(status().isOk());
return this;
}
public MockBase jsonExpect(String template, Object value) throws Exception {
resultActions = resultActions.andExpect(jsonPath(template).value(value));
return this;
}
public MockBase doPrint() throws Exception {
resultActions = resultActions.andDo(print());
return this;
}
}
MockBase
定义为抽象类是其他原因,这里无需关注。上述代码可以发现,perform
、okExpect
等方法都才用了Builder模式,返回了自身的引用,所以可以实现链式编程。
第一颗银弹
熟悉使用Mockito进行单元测试的朋友可能会觉得上述代码很眼熟,实际上该类就是对Mockito提供的方法实现了进一步的封装,从而简化编程。在子类使用时我们发现,基类的jsonExpect方法虽然通用,但是每次写相同的JSONPATH是一件很痛苦的事情,所以我们希望对常用的预测返回值进行进一步封装,写一个增强类,对MockBase
的方法进行扩展。如果我们不对基类进行改造,那么写出来的增强类应该是这样的:
public abstract class MockStrengthen extends MockBase {
public MockStrengthen resultOk() throws Exception {
return jsonExpect("$.ok", true);
}
public MockStrengthen resultFail() throws Exception {
return jsonExpect("$.ok", false);
}
public MockStrengthen respCode(int value) throws Exception {
return jsonExpect("$.respCode", value);
}
}
协变返回类型
MockStrengthen
虽然对基类方法进行了扩展,但是因为基类的方法返回的是MockBase
的引用,所以调用基类的方法后无法再调用子类,也就是说不再支持链式编程,所以需要对基类进行改造,代码如下:
public abstract class MockBase<T> {
protected MockMvc mockMvc;
protected ResultActions resultActions;
protected abstract String getRootMapping();
private T self() {
return (T) this;
}
public T perform(RequestBuilder builder) throws Exception {
resultActions = this.mockMvc.perform(builder);
return self();
}
public T okExpect() throws Exception {
resultActions = resultActions.andExpect(status().isOk());
return self();
}
public T jsonExpect(String template, Object value) throws Exception {
resultActions = resultActions.andExpect(jsonPath(template).value(value));
return self();
}
public T doPrint() throws Exception {
resultActions = resultActions.andDo(print());
return self();
}
}
此时MockStrengthen
变为:
public abstract class MockStrengthen extends MockBase<MockStrengthen> {
public MockStrengthen resultOk() throws Exception {
return jsonExpect("$.ok", true);
}
public MockStrengthen resultFail() throws Exception {
return jsonExpect("$.ok", false);
}
public MockStrengthen respCode(int value) throws Exception {
return jsonExpect("$.respCode", value);
}
}
此时子类不仅对基类方法进行了扩展,而且支持链式编程,优雅地解决了上述问题。
思路介绍
实现链式编程的关键是返回指向增强类的引用,那么如何让父类知道哪个子类需要支持链式编程呢?最简单的方式就是通过泛型把子类类型传给父类,即:
class MockStrengthen extends MockBase<MockStrengthen>
支持链式编程的方法因为需要返回指向子类的引用,所以需要进行一步强制转换。