Java 中的 Builder 模式和协变返回类型

通过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定义为抽象类是其他原因,这里无需关注。上述代码可以发现,performokExpect等方法都才用了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>

支持链式编程的方法因为需要返回指向子类的引用,所以需要进行一步强制转换。

参考资料

Java 中的 Builder 模式和协变返回类型

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值