以选秀节目为例替换代码中的if-else

38次阅读

共计 3776 个字符,预计需要花费 10 分钟才能阅读完成。

前言

起源是看了一篇文章在 Spring Boot 中,如何干掉 if else!,有兴趣的可以看下。感觉工作中也经常处理类似的问题,就整理了一下。

选秀节目只是当作一个场景,并不会讲太多,如果你真的有兴趣也可以联系我(认真脸。

选手分组

背景

选秀节目事前会让参赛者填一份资料,除了基本信息,还有你的定位,比如主唱、主舞、rap,节目组需要对参赛选手进行一个简单的分组。

实现

定义一个参赛选手类

@Data
public class User {

    private Long id;

    private String name;

    private int age;

    private Position position;
}

Position 是个枚举

public enum Position {
    /**
     * 主唱
     */
    VOCAL,
    /**
     * 主舞
     */
    DANCE,
    /**
     * RAP
     */
    RAP
}

定义一个分组的接口

public interface IGroupService {void group(User user);
    
}

最直接的实现方式就是 if else 或者 switch

@Slf4j
@Service
public class GroupServiceImpl implements IGroupService {
    @Override
    public void group(User user) {if (user.getPosition() == Position.VOCAL) {log.info("{}({})划分到主唱组", user.getName(), user.getId());
        }
        if (user.getPosition() == Position.DANCE) {log.info("{}({})划分到主舞组", user.getName(), user.getId());
        }
        if (user.getPosition() == Position.RAP) {log.info("{}({})划分到 RAP 组", user.getName(), user.getId());
        }
    }
}

优化

可以定义一个 handler

public interface IGroupHandler {void group(User user);

    /**
     * 能处理的定位
     *
     * @return
     */
    Position getPosition();}

再写三个实现

@Slf4j
@Component
public class VocalGroupHandler implements IGroupHandler {
    @Override
    public void group(User user) {log.info("{}({})划分到主唱组", user.getName(), user.getId());
    }

    @Override
    public Position getPosition() {return Position.VOCAL;}
}
@Slf4j
@Component
public class DanceGroupHandler implements IGroupHandler {
    @Override
    public void group(User user) {log.info("{}({})划分到主舞组", user.getName(), user.getId());
    }

    @Override
    public Position getPosition() {return Position.DANCE;}
}
@Slf4j
@Component
public class RapGroupHandler implements IGroupHandler {
    @Override
    public void group(User user) {log.info("{}({})划分到 RAP 组", user.getName(), user.getId());
    }

    @Override
    public Position getPosition() {return Position.RAP;}
}

然后在服务类里注入这些 handler,在处理的时候通过每个 handler 能处理的类型,找到具体的 handler

@Service
public class GroupServiceV2Impl implements IGroupService {
    public Map<Position, IGroupHandler> handlerMap;

    public GroupServiceV2Impl(List<IGroupHandler> groupHandlers) {handlerMap = groupHandlers.stream().collect(Collectors.toMap(IGroupHandler::getPosition, Function.identity()));
    }

    @Override
    public void group(User user) {IGroupHandler groupHandler = handlerMap.get(user.getPosition());
        if (groupHandler == null) {throw new IllegalArgumentException("无法处理的 position:" + user.getPosition());
        }
        groupHandler.group(user);
    }
}

这里利用了 spring 的依赖注入,通过构造器注入把所有的 handler 进行注入。

后续如果 Position 有变更,只需要新增或修改相应的 handler 类即可。

选手评级

背景

选秀节目最开始会对选手评级,A、B、C、D、E、F 几个等级。

导师们也都有各自擅长的领域,比如评价分为几个方面,唱跳 rap,没有篮球。

可以假定一个评价规则

等级 评价规则
A 3 项都超过 90 分
B 2 项都超过 90 分
C 1 项超过 90 分
D 平均分 80 以上
E 平均分 75 以上
F 不符合以上的都是 F

分析

跟刚才的有些类似,但不同点是之前的条件是互斥的,但这里的条件并不是,满足高等级的很可能满足低等级,比如 A 等级的满足其他的所有条件,所有人也都可以划分为 F 等级。

所以这里处理是有顺序的,不满足高等级才会分到低等级。

实现

定义评级

public enum Rate {A, B, C, D, E, F}

定义对选手的评价

@Data
public class Score {

    private Long uid;

    private int vocal;

    private int dance;

    private int rap;
}

定义获取评级的接口

public interface IRateService {Rate getRate(Score score);
    
}

按照刚才的思路,我们也可以定义每个级别 handler

public interface IRateHandler {Rate getRate();

    /**
     * 条件是否满足
     *
     * @return
     */
    boolean isMatch(Score score);
}

实现就不都写了,要注意的是 F 等级应该是默认是 true

@Component
public class RateAHandler implements IRateHandler {
    @Override
    public Rate getRate() {return Rate.A;}

    @Override
    public boolean isMatch(Score score) {return score.getVocal() > 90 && score.getDance() > 90 && score.getRap() > 90;
    }
}
@Component
public class RateFHandler implements IRateHandler {
    @Override
    public Rate getRate() {return Rate.F;}

    @Override
    public boolean isMatch(Score score) {return true;}
}

服务的实现与刚才类似,但是由于现在的条件不是那么单一的判断,无法使用 map

@Service
public class RateServiceImpl implements IRateService {

    public List<IRateHandler> rateHandlers;

    public RateServiceImpl(List<IRateHandler> rateHandlers) {this.rateHandlers = rateHandlers;}

    @Override
    public Rate getRate(Score score) {for (IRateHandler rateHandler : rateHandlers) {if (rateHandler.isMatch(score)) {return rateHandler.getRate();
            }
        }
        throw new IllegalArgumentException("无法处理");
    }
}

这里还有个问题,就是 handler 应该是有顺序的,有两种实现思路

  • 在 handler 上加 @Order 注解,在注入的时候就已经排好序了
  • IRateHandler 上定义一个获取优先级的方法,然后在 RateServiceImpl 的构造函数里,根据优先级进行排序。

然后就搞定了。

后记

这种实现方式的使用场景还是很多的,应对更复杂的场景还可以提供一些公共的抽象类,减少一些重复的代码。

如果你有更好的实现方式也欢迎留言。

看到了这里一定是真爱了,关注微信公众号【憨憨的春天】第一时间获取更新

正文完
 0