前言
起源是看了一篇文章在 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
的构造函数里,根据优先级进行排序。
然后就搞定了。
后记
这种实现方式的使用场景还是很多的,应对更复杂的场景还可以提供一些公共的抽象类,减少一些重复的代码。
如果你有更好的实现方式也欢迎留言。
看到了这里一定是真爱了,关注微信公众号【憨憨的春天】第一时间获取更新