活动参与限制条件实现、我:if else 粘就完事了!!!

173 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

现实

比如你参与的某个活动中、你高高兴兴的去参加活动、你刚进入到页面发现他弹出来一个“仅限xxx参与活动”、然后引导你去另一个活动、发现另一个活动能进入到页面中、就当你高高兴兴点击领取薅它羊毛的时候、那个加载圈在哪转啊转啊!!!结果它又弹出一个“您当前会员等级不符合领取条件限制”。

就是很多时候、当你参与的时候会校验你的一些身份信息、符不符合条件之类的、假设让你搞一套通用的活动限制、领取礼品的限制、你总不可能每个活动都去写一套之前写过的条件限制、加一堆if else、这样是很不合理的。

场景简介

假设我有一个活动、要限制参与人的条件限制、如:某某会员能参加、后续会有多个活动、每个活动需要配置不同的限制、活动中的礼品也符合条件限制的人才能领取到。

限制统一由配置化、如:是否VIP、是否多少多少级会员能参与、无非就两种、一个单选、一个多选限制满足

1669551577203.png

或者活动层面在加一个限制条件是”全部满足“或者”任意满足“条件、判断是活动的全部限制。

1669602033572.png

基本的话以上这些就可以满足了、但是条件苛刻的话可以在多选的层面在加一个、“并且”、“或者”。

1669709485004.png

由以上这些条件就能设想一个活动限制、而这些限制要单独拎出来、由每个活动来配置需要限制条件。

开始实现

如果没有开始设计之前、肯定都是少量的判断配置、如是不是会员之类、最简单最快的方式就在指定的地方手动写代码配置需要的限制。

举个栗子、如进入页面的时候我要判断是不是会员.....等一些其他限制。

 @RequestMapping("index")
 public String index() {
   if("限制会员的值" != "我的会员"){
     System.out.println(”不是会员、不能进入页面“);
   }else if("限制xxxx的值" != "我的xxx"){
     System.out.println(”不是xxx、不能进入页面“);
   }else{
      System.out.println(”通过限制“);
   }
   return "free_activities/index";
 }

可以看出写一堆的if的判断、前期可能大部分人都是这样写、但是活动页面一旦多起来、就粘贴一堆重复的东西(你也想写10多个if else嘛??)、这些还不算什么、如果一些限制的值的枚举需要改之类的这种才要命、一堆活动都要你一个一个改、已经可以看出两个问题了1、大量if else 2、活动限制的值写死。

解决if else...

无非就是用一些设计模式中的一些思路、首先定义一个接口、由各种限制的实现类去实现这个接口并重写这个接口中的方法、使用起来就可以去根据对应限制的类型去找到对应限制的实现方法、说句人话就是回家各找各妈。

 /**
  * 活动限制
  * @param <LimitType> 限制数据的类型
  */
 public interface LimitAdapter<LimitType> {
     /**
      * 是否进行要进行逻辑处理。
      * @return true 无限制跳过操作、false 有限制
      */
     Boolean isNotMust();
 
     /**
      * 得到限制的值
      * @return 限制的value值
      */
     LimitType getLimitValue();
 
     /**
      * 执行处理【用户是否符合限制】
      * @return  true 符合、false 不符合。
      */
     Boolean execute();
 }

1669551577203.png

上面也看了、这种限制无非就单选或者多选限制、所以可以在抽象出一个单选处理类、以及一个多选处理的类

 public abstract class SingleLimit implements LimitAdapter<Integer> {
     /**
      * 是否限制、做为排除使用、因为限制的值为空就证明不用校验。
      * @return true 不限制、false 限制
      */
     @Override
     public Boolean isNotMust() {
         return null == getLimitValue();
     }
 }
 public abstract class MultiLimit implements LimitAdapter<List<String>> {
     /**
      * 是否限制、做为排除使用、因为限制的值为空就证明不用校验
      * @return true 不限制、false 限制
      */
     @Override
     public Boolean isNotMust() {
         return null == getLimitValue();
     }
 }

因为没有很复杂的处理、在isNotMust方法中只做了判断空的操作、接下来就是看各种限制的实现。

掘金会员实现类

 @Slf4j
 public class Vip extends SingleLimit {
     /**
      * 得到配置的限制、我们这边写死了限制的值
      * @return 限制值
      */
     @Override
     public Integer getLimitValue() {
         return 1;
     }
     /**
      * 处理用户是否是会员
      * @return true 是会员、false不是会员。
      */
     @Override
     public Boolean execute() {
         // 拿到当前用户、可以通过第三方或者本地库获取用户会员信息
         Account account = AccountContext.getAccount();
         // 拿到当前的限制值
         Integer limitValue = getLimitValue();
         if (!Objects.equals(limitValue, account.getIsVip())) {
             log.error("校验用户:[{}]是否会员异常、限制的值:[{}]、用户会员信息:[{}]", account.getId(), limitValue, account.getIsVip());
             return false;
         }
         return true;
     }
 }

掘友等级实现类

 @Slf4j
 public class Level extends MultiLimit {
 
     /**
      * 得到配置的限制、我们这边写死了限制的值
      * @return 限制值
      */
     @Override
     public List<String> getLimitValue() {
         return Arrays.asList("JY1", "JY2");
     }
     /**
      * 处理用户是否是匹配等级
      * @return true 匹配、false不匹配。
      */
     @Override
     public Boolean execute() {
         // 拿到当前用户信息、可以通过第三方或者本地库获取用户会员信息
         Account account = AccountContext.getAccount();
         // 拿到当前的限制值
         List<String> limitValue = getLimitValue();
         // 通过第三方或者本地库获取用户会员等级
         String level = account.getLevel();
         if (!limitValue.contains(level)) {
             log.error("校验用户:[{}]是否匹配等级异常、限制的值:{}、用户会员信息:[{}]", account.getId(), limitValue, level);
             return false;
         }
         return true;
     }
 }

功能测试

明确限制信息、限制是VIP的、掘友等级JY1、或者JY2、明确用户信息不是会员、掘友等级JY1

 @Slf4j
 public class Demo {
 
     private static List<LimitAdapter<?>> limitAdapter = new ArrayList<>();
     static {
         limitAdapter.add(new Vip());
         limitAdapter.add(new Level());
     }
 
     public static void main(String[] args) {
         // 用户数据
         Account account = new Account();
         account.setId(121L);
         account.setName("小明");
         account.setIsVip(0);
         account.setLevel("JY1");
         AccountContext.setAccount(account);
       
         LimitAdapter<?> vip = new Vip();
         if (vip.isNotMust()) {
             log.info("VIP限制为空、不做处理");
         } else {
             if (vip.execute()) {
                 log.info("校验VIP通过");
             } else {
                 log.info("校验VIP未通过");
             }
         }
         LimitAdapter<?> level = new Level();
         if (level.isNotMust()) {
             log.info("等级限制为空、不做处理");
         } else {
             if (level.execute()) {
                 log.info("校验等级通过");
             } else {
                 log.info("校验等级未通过");
             }
         }
     }
 }

1669902798635.png

会发现上面代码的实现类是一个一个new的、我们可以把它放在一个集合中、统一遍历处理限制。

 @Slf4j
 public class Demo {
     private static List<LimitAdapter<?>> limitAdapter = new ArrayList<>();
     static {
         limitAdapter.add(new Vip());
         limitAdapter.add(new Level());
     }

     public static void main(String[] args) {
         // 用户数据
         Account account = new Account();
         account.setId(121L);
         account.setName("小明");
         account.setIsVip(0);
         account.setLevel("JY1");
         AccountContext.setAccount(account);
 
         for (LimitAdapter<?> adapter : limitAdapter) {
             if (adapter.isNotMust()) {
                 log.info("限制为空、不做处理");
             } else {
                 if (adapter.execute()) {
                     log.info("校验通过");
                 } else {
                     log.info("校验未通过");
                 }
             }
         }
     }
 }

其实也可以使用Spring容器将对应的实现类注入到容器中、后续直接通过@Autowired注入、要注意的就是实现类要加@Component

 @Autowired
 private List<LimitAdapter<?>> limitAdapters;

其实这种不是一个完全把if else去除的、这样做的原因是为了好管理、不会让你的编写的限制条件左一块右一块、减少你对需求的改动一点就离职的心情。(没这样做之前:哪个**做的、*到底这个人会不会写代码、写的跟*一样、**这还有一块要改、**那还有一块要改、**谁爱改谁改、**不改了、*、明天离职!)

解决活动限制的值

 @Override
 public Integer getLimitValue() {
   return 1;
 }

从上面代码可以看出我们的限制的值是写死了、以及我们上方‘场景简介’介绍的配置化的这个也没有一个很好完善、后需会出另一个版本的代码、做到一个单独的活动限制模块、可以动态的拔插活动限制、以及动态拔插限制选项。