V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nicepink
V2EX  ›  程序员

[ Java ] 方法里有很多判断需要提前结束,很多重复代码,求大神!

  •  4
     
  •   nicepink · 2021-10-21 13:56:21 +08:00 · 3385 次点击
    这是一个创建于 1134 天前的主题,其中的信息可能已经有所发展或是发生改变。

    因为几个查询条件耗时久查到结果就提前结束,但是有重复的地方觉得不够优雅 if 判断那一块

        private void prepareBookingOffice(BrChangedEvent brChangedEvent) {
            BrGeneralInfoDto brGeneralInfoDto = Optional.ofNullable(brChangedEvent)
                    .map(BrChangedEvent::getBrDto)
                    .map(BrDto::getGeneralInfo)
                    .orElse(BrGeneralInfoDto.builder().build());
    
            if (StringUtils.isNotEmpty(brGeneralInfoDto.getBookingOfficeCode())) {
                return;
            }
    
            // 耗时
            String officeCode = getOfficeCodeByOfficeUnlLocCode(brGeneralInfoDto);
            // 重复
            if (StringUtils.isNotEmpty(officeCode)) {
                brGeneralInfoDto.setBookingOfficeCode(officeCode);
                return;
            }
    
            // 耗时
            officeCode = getOfficeCodeByPor(brChangedEvent);
            // 重复
            if (StringUtils.isNotEmpty(officeCode)) {
                brGeneralInfoDto.setBookingOfficeCode(officeCode);
                return;
            }
    
            // 耗时
            officeCode = getOfficeCodeByFnd(brChangedEvent);
            // 重复
            if (StringUtils.isNotEmpty(officeCode)) {
                brGeneralInfoDto.setBookingOfficeCode(officeCode);
                return;
            }
    
            // 耗时
            officeCode = getOfficeCodeByPol(brChangedEvent);
            // 重复
            if (StringUtils.isNotEmpty(officeCode)) {
                brGeneralInfoDto.setBookingOfficeCode(officeCode);
            }
        }
    
    第 1 条附言  ·  2021-10-21 15:57:35 +08:00

    有些回复有些不太理解(知识储备不够...),后续再研究研究

    目前先尝试了这位老哥的写法:

    public class OptionalChain<T> {
    
        private T value;
    
        private OptionalChain(T val) {
            value = val;
        }
    
        public static <T> OptionalChain<T> of(Supplier<T> supplier) {
            return new OptionalChain<>(supplier.get());
        }
    
        public OptionalChain<T> or(Supplier<T> supplier) {
            if (StringUtils.isEmpty(value)) {
                value = supplier.get();
            }
            return this;
        }
    
        public T get() {
            return value;
        }
    
    }
    
    private void prepareBookingOffice(BrChangedEvent brChangedEvent) {
        BrGeneralInfoDto brGeneralInfoDto = Optional.ofNullable(brChangedEvent)
                .map(BrChangedEvent::getBrDto)
                .map(BrDto::getGeneralInfo)
                .orElse(BrGeneralInfoDto.builder().build());
    
        brGeneralInfoDto.setBookingOfficeCode(OptionalChain.of(() -> brGeneralInfoDto.getBookingOfficeCode())
                .or(() -> getOfficeCodeByOfficeUnlLocCode(brGeneralInfoDto))
                .or(() -> getOfficeCodeByPor(brChangedEvent))
                .or(() -> getOfficeCodeByFnd(brChangedEvent))
                .or(() -> getOfficeCodeByPol(brChangedEvent))
                .or(() -> StringUtils.EMPTY)
                .get());
    
    }
    
    

    补充一下:

    • 这个问题起因是因为昨天 code review 的时候同事觉得我这里可以改进,但是没有思路
    • 这里耗时的地方其实也就是 call 其他服务去拿数据,顺序要固定,是业务上的要求
    • 有些老哥说觉得没必要改,其实我也在想有没有必要,主要是想让代码既清楚又不冗余
    23 条回复    2021-10-21 23:04:15 +08:00
    itning
        1
    itning  
       2021-10-21 13:59:57 +08:00
    封装 继承 多态
    aguesuka
        2
    aguesuka  
       2021-10-21 14:03:37 +08:00
    把耗时函数改成 lambda 保存到 List 中
    zoharSoul
        3
    zoharSoul  
       2021-10-21 14:07:07 +08:00
    没什么问题 挺好看懂的
    zsl199512101234
        4
    zsl199512101234  
       2021-10-21 14:11:03 +08:00   ❤️ 1
    二楼的方法,把耗时函数改成 lambda 保存到 list 中去,然后循环调用直到不为空就赋值并且 break
    zjsxwc
        5
    zjsxwc  
       2021-10-21 14:12:18 +08:00
    for 循环遍历 List
    chendy
        6
    chendy  
       2021-10-21 14:12:33 +08:00
    第一步,把“取 officeCode”抽出去,如果方法不是很多而且也不怎么调整,做到这里就可以了

    private String getOfficeCode(BrChangedEvent e){
    String code = findOfficeCodeByA(e);
    if (!StringUtil.isEmpty(code)) {
    return code;
    }
    // 后面的差不多
    }

    如果方法比较多且需要调整,那么就进一步抽

    private List<Function<BrChangedEvent, String>> codeFinders;

    {
    codeFinders.add(this::findOfficeCodeByA);
    codeFinders.add(this::findOfficeCodeByB);
    codeFinders.add(this::findOfficeCodeByC);
    }


    private String getOfficeCode(BrChangedEvent e) {
    for(Function<BrChangedEvent, String>> finder : codeFinders) {
    String code = finder.apply(codeFinders);
    if(!StringUtil.isEmpty(code)){
    return code;
    }
    return null;
    }
    }
    admol
        7
    admol  
       2021-10-21 14:12:42 +08:00
    如果你只是觉得 if 有点多,给你提供一个思路,你看是否可行

    public static void main(String[] args){
    String officeCode = null;
    if(Objects.nonNull(officeCode = a()) || Objects.nonNull(officeCode = b())|| Objects.nonNull(officeCode = c())){
    }else{
    officeCode = "default value";
    }
    System.out.println("officeCode:"+officeCode);
    }

    private static String c(){
    System.out.println("C");
    return "C";
    }
    private static String b(){
    System.out.println("B");
    return "B";
    }

    private static String a(){
    System.out.println("A");
    return null;
    }
    zsl199512101234
        8
    zsl199512101234  
       2021-10-21 14:14:31 +08:00
    ```java
    @FunctionalInterface
    public interface OfficeCodeService {
    BookingOfficeCode getOfficeCode(BrGeneralInfoDto brGeneralInfoDto);
    }
    ```
    mango88
        9
    mango88  
       2021-10-21 14:15:15 +08:00
    耗时操作有优先级要求吗 ?

    还是只要任一返回值就可以了 ? 如果是任一个操作返回就满足需求,可以试试用 CompletableFuture.anyOf()
    uCharles
        10
    uCharles  
       2021-10-21 14:15:48 +08:00
    试试抽取之后用 Optional 来进行判断
    mango88
        11
    mango88  
       2021-10-21 14:18:17 +08:00
    @mango88 每一步耗时操作可能会有空值返回 这样就不行了。

    理解错了,6L 的方案会好点。
    ipwx
        12
    ipwx  
       2021-10-21 14:18:21 +08:00
    不懂 Java,伪代码

    interface IAction { String run(); }

    class GetOfficeCodeByOfficeUnlLocCodeAction implements IAction { ... };
    class GetOfficeCodeByPorAction implements IAction { ... };

    。。。


    List<IAction> actions = {
    new GetOfficeCodeByOfficeUnlLocCodeAction(...),
    new GetOfficeCodeByPorAction(...)
    };

    for (action in actions) {
    String officeCode = getOfficeCodeByOfficeUnlLocCode(brGeneralInfoDto);
    // 重复
    if (StringUtils.isNotEmpty(officeCode)) {
    brGeneralInfoDto.setBookingOfficeCode(officeCode);
    return;
    }
    }
    wolfie
        13
    wolfie  
       2021-10-21 14:20:26 +08:00
    在 Optional 基础上,定义一个 OptionalWrapper 。

    OptionalWrapper<T> or(Supplier<T> supplier)

    可以链式调用。
    Guiyanakuang
        14
    Guiyanakuang  
       2021-10-21 14:23:14 +08:00
    为了保证可读性不建议抽取保存到 list 里,只需要将重复部分独立为单个函数,每个函数返回 this,链式调用逻辑更清晰
    wolfie
        15
    wolfie  
       2021-10-21 14:31:14 +08:00   ❤️ 3
    ```
    class OptionalChain<T> {

    private T value;

    private OptionalChain(T val) {
    value = val;
    }

    public static <T> OptionalChain<T> of(Supplier<T> supplier) {
    return new OptionalChain<>(supplier.get());
    }

    public OptionalChain<T> or(Supplier<T> supplier) {
    if (value == null) {
    value = supplier.get();
    }
    return this;
    }

    public T get() {
    return value;
    }

    }
    ```
    hingbong
        16
    hingbong  
       2021-10-21 16:30:40 +08:00
    ```
    public OptionalChain<T> or(Predicate<T> predicate, Supplier<T> supplier) {
    if (predicate.test()) {
    return this;
    }
    value = supplier.get();
    }
    ```
    判断条件也动态起来?
    fkdog
        17
    fkdog  
       2021-10-21 16:30:43 +08:00
    将所有的 getOfficeCodeByXXX()包装成 Function,然后塞进一个 List 。
    做 list 的迭代,执行 function.apply(),如果返回值非空,则 break 跳出迭代。

    ``` java
    List<Function<BrChangedEvent,String>> funcs= Arrays.asList(
    XXX::getOfficeCodeByOfficeUnlLocCode,
    XXX::getOfficeCodeByPor,
    XXX::getOfficeCodeByFnd,
    XXX::getOfficeCodeByPol);
    for(Function<BrChangedEvent,String> func:funcs){
    String officeCode = func.apply(brGeneralInfoDto);
    if(String.isNotEmpty(officeCode)){
    brGeneralInfoDto.setBookingOfficeCode(officeCode);
    }
    }
    ```
    NeroKamin
        18
    NeroKamin  
       2021-10-21 17:10:37 +08:00
    @wolfie 第一直觉也是把 function 放到 list 遍历,再看这个链式调用,明显感觉要好很多
    yazinnnn
        19
    yazinnnn  
       2021-10-21 20:05:04 +08:00
    functions.stream()
    .parallel()
    .map(it -> it.apply(""))
    .filter(StringUtils::isNotBlank)
    .findFirst()
    .orElse("");

    这样?
    oneisall8955
        20
    oneisall8955  
       2021-10-21 20:54:46 +08:00 via Android
    remark,这样优化也有意义也有意思,后面再添加就不断的 append 进去
    crclz
        21
    crclz  
       2021-10-21 22:19:07 +08:00
    初始实现没啥问题,第二版过拟合了(现在最简,但是不能很好应对新需求)
    bxb100
        22
    bxb100  
       2021-10-21 22:40:45 +08:00
    可以考虑使用责任链设计模式
    jorneyr
        23
    jorneyr  
       2021-10-21 23:04:15 +08:00
    if 已经是最优解了,其他各种花哨的办法难看的要死。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2814 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 15:29 · PVG 23:29 · LAX 07:29 · JFK 10:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.