起源是看了一篇文章 在 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应该是有顺序的,有两种实现思路
@Order
注解,在注入的时候就已经排好序了 IRateHandler
上定义一个获取优先级的方法,然后在 RateServiceImpl
的构造函数里,根据优先级进行排序。 然后就搞定了。
这种实现方式的使用场景还是很多的,应对更复杂的场景还可以提供一些公共的抽象类,减少一些重复的代码。
如果你有更好的实现方式也欢迎留言。