CR 完同事的代码,我决定把这个工具类偷出来

2,969 阅读4分钟

1. 引言

在本周五,集中代码 CR 的时候

小王写的一段代码引起了领导的质问

public class InsuranceOrderDTO {
    // 保单号
    private String policyNo;
    // 关联人信息
    private InsRelationshipDTO insRelationShipDTO;
    // 保单 url
    private String policyUrl;
    // 发票信息
    private InvoiceDTO invoiceDTO;
}

领导:“我看你这个初始化的方法,是嵌入在各个流程中,但是有的地方只用了保单的 url,有些地方只用了发票信息,你这统一初始化了上下文,不是浪费资源么,能少一次外调就少一次外调”

小王:“领导,我不这样写,需要的时候才去查,这个查询的代码会整的到处都是,不优雅啊,我这边初始化后,上下文中直接用,多完美啊”

领导:“你这不行啊,耗费了无用的 rt”

小王:“领导,这点 rt 算不了什么啊”

在小王和领导 battle 的时候,我就知道,这个时候,我装x 的时候到了

我:“领导,我觉得可以实用惰性加载来解决一下这个问题”

小王白了我一眼,“又整什么花活”

领导:“小杨,你展开说说”

2. 惰性加载

我:“何谓惰性加载,即在我们切实需要某份数据的时候,再去执行查询或计算动作,加载数据。而不是预先直接执行某个查询或者计算动作,然后将数据放在上下文。

快过年了,举个例子,你是现在直接去银行取钱包个红包放在身上方便,还是说等真正要去包红包,再去银行取钱方便呢?答案当然是,不包红包的方便了”

当这个梗没有响的时候,我发现会议室的气氛有点尴尬了,清了清嗓子

“其实 Java 中已经有了 惰性计算的机制,我们可以这样写”

Supplier<String> policyUrlSupplier = () -> queryPolicyUrl(policyNo)
String policyUrl = policyUrlSupplier.get()    

类比到我们的类中可以这样改造

public class InsuranceOrderDTO {
    // 保单号
    private String policyNo;
    // 关联人信息
    private InsRelationshipDTO;
    // 保单 url
    private String policyUrl;
    // 发票信息
    private InvoiceDTO invoiceDTO;
  
    public String getPolicyUrl() {
      policyUrl = () -> policyUrlService.queryPolicyUrl(policyNo).get();
    }
  
    .......
}

此时,小王有点不服气 “你这样写不行啊,我放在上下文,撑死了最多查一次,你这样每次都得去 get ,重新查询”

我有点无语了,我就是写个 demo,这小王的脑袋瓜咋转不过来

领导:“你这个思路可以,来拓展成一个工具类,可以在组内推广一下”

我:“不愧是领导你啊,还得是你有远见,我立马改一下”

2.1 通用实现

public class LazyBuilder<T> implements Supplier<T> {
​
    private final Supplier<T> supplier;
​
    private T value;
​
    public LazyBuilder(Supplier<T> supplier) {
        this.supplier = supplier;
    }
​
    public static <T> LazyBuilder<T> build(Supplier<T> supplier) {
        return new LazyBuilder<>(supplier);
    }
  
    @Override
    public T get() {
        return orElse(null);
    }
​
    public T orElse(T defaultValue) {
        // 缓存值不存在,重新查询
        if (Objects.isNull(value)) {
            value = Optional.ofNullable(supplier.get()).orElse(defaultValue);
        } 
        return value;
    }
​
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        // 缓存值不存在,重新查询
        if (Objects.isNull(value)) {
            value = Optional.ofNullable(supplier.get()).orElseThrow(exceptionSupplier);
        }
        return value;
    }
}

领导:“小杨这代码写的可以,很优雅。小王,你多跟人家学学,你就照着小杨的这一段代码,把你的 InsuranceOrderDTO 修改一下,我看你有没有学习到精髓”

小王:“emmm,好的领导”

public class InsuranceOrderDTO {
    // 保单号
    private LazyBulider<String> policyNo;
    // 关联人信息
    private LazyBuilder<InsRelationshipDTO> insRelationshipDTO;
    // 保单 url
    private LazyBuilder<String> policyUrl;
    // 发票信息
    private LazyBuilder<InvoiceDTO> invoiceDTO;
    
    
    ...
}
​
InsuranceOrderDTO insOrderDTO = new InsuranceOrderDTO()
insOrderDTO.setPolicyNo(LazyBuilder.build(() -> policyService.queryPolicy(userId)))
  

小王:领导,不对有问题啊,我这个保单号也惰性加载了,然后我想去获得保单的 url 有需要惰性加载,我似乎要变成这样了

insOrderDTO.setPolicyUrl(LazyBulider.build() -> policyUrlService.queryUrl(insOrderDTO.getPolicyNo()))  

小王:这样一弄,我的保单号立马去查询了,没有意义了啊

我:“这个好弄啊,你这样改一下就可以”

public <S> LazyBulider<S> map(Function<T, S> function) {
      return LazyBulider.of(() -> function.apply(get()));
}

我:那么获取保单url 就是这样了

insOrderDTO.setPolicyUrl(insOrderDTO.getPolicyNo().map(polciNo -> LazyBulider.build() -> policyUrlService.queryUrl(policyNo)))                                             

此时此刻,领导为我鼓起了掌。

我知道了,今天这个 b 被我装到了。挥一挥衣袖,“没有啦,是领导平时教导有方”

3. 完整 LazyBuilder 工具类

既然,你看到这里了,我建议你收藏一波,因为今天是我装 x,但是当你用了这个工具类,下一个装 x 的就是你了

我行,你也行

public class LazyBuilder<T> implements Supplier<T> {

    private final Supplier<T> supplier;

    private T value;

    public LazyBuilder(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public static <T> LazyBuilder<T> build(Supplier<T> supplier) {
        return new LazyBuilder<>(supplier);
    }

    @Override
    public T get() {
        return orElse(null);
    }

    public T orElse(T defaultValue) {
        // 缓存值不存在,重新查询
        if (Objects.isNull(value)) {
            value = Optional.ofNullable(supplier.get()).orElse(defaultValue)
        }
        return value;
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        // 缓存值不存在,重新查询
        if (Objects.isNull(value)) {
            value = Optional.ofNullable(supplier.get()).orElseThrow(exceptionSupplier);
        }
        return value;
    }

    public <S> LazyBuilder<S> map(Function<T, S> function) {
        return LazyBuilder.build(() -> function.apply(get()));
    }
}

4. 闲言碎语

这周,去公司健身房,碰到一老外

身上肌肉练的和灭霸一样,我正举着铁的呢,通过镜子,我发现这老外一直盯着我的屁股看

我正在寻思委婉又不礼貌的用 English 交涉一下,让这老外礼貌一点,别看我屁股

结果老外过来用流利的中文说:同学你好,你的裤子上粘着我的手套

尬住了。。。