背景

根据优惠券类型resourceType -> 确定查询哪个数据表
根据编码resourceId -> 到对应的数据表里边查询优惠券的派发方式grantType和领取规则
优惠券有多种类型,分别对应了不同的数据库表:

红包 —— 红包发放规则表
购物券 —— 购物券表
QQ会员
外卖会员
实际的优惠券远不止这些,这个需求是要我们写一个业务分派的逻辑

首先能想到的思路就是if-else或者switch case

public  String getNameByType(String resourceType) {
        if ("红包".equals(resourceType)) {
           return "每周末9点发放";
        } else if ("购物券".equals(resourceType)) {
            return "每周三9点发放";
        } else if ("qq会员".equals(resourceType)) {
            return "每周一0点开始秒杀";
        } else {
            return "查询不到该优惠券的发放方式";
        }
    }

如果要这么写的话, 一个方法的代码可就太长了,影响了可读性。(别看着上面case里面只有一句话,但实际情况是有很多行的)

而且由于整个 if-else 的代码有很多行,也不方便修改,可维护性低。

策略模式

策略模式是把if语句里面的逻辑抽出来写成一个类,如果要修改某个逻辑的话,仅修改一个具体的实现类的逻辑即可,可维护性会好不少。

  • 策略类的公共抽象类
public abstract class AbsStragegy {
    //定义支持所有算法的公共接口
    public abstract String query();
}

  • 策略类的具体实现
    RedPaper
public class RedPaper extends AbsStragegy{
    /**
     * 查询红包的
     *
     * @return {@link String}
     */
    @Override
    public String query() {
        return "每周末9点发放";
    }
}

Shopping

public class Shopping extends AbsStragegy{
    /**
     * 购物券的发放方式
     *
     * @return {@link String}
     */
    @Override
    public String query() {
        return "每周三9点发放";
    }
}

QQVip

public class QQVip extends AbsStragegy {
    /**
     * qq会员的发放方式
     *
     * @return {@link String}
     */
    @Override
    public String query() {
        return "每周一0点开始秒杀";
    }
}
  • 客户端调用的接口
public class Context {
    AbsStragegy absStragegy;
    public Context(AbsStragegy absStragegy){
        this.absStragegy=absStragegy;
    }
    public String ContextQuery(){
       return this.absStragegy.query();
    }
}

策略模式的实现

 public String getNameByType2(String type) {
        switch (type) {
            case "红包" -> {
                Context context = new Context(new RedPaper());
                return context.ContextQuery();
            }
            case "购物券" -> {
                Context context = new Context(new Shopping());
                return context.ContextQuery();
            }
            case "qq会员" -> {
                Context context = new Context(new QQVip());
                return context.ContextQuery();
            }
            default -> {
                return "查询不到该优惠券的发放方式";
            }
        }
    }

缺点明显:
如果 if-else的判断情况很多,那么对应的具体策略实现类也会很多,上边的具体的策略实现类还只是3个,查询红包发放方式写在类RedPaper里边,购物券写在另一个类Shopping里边;那资源类型多个QQ会员和外卖会员,不就得再多写两个类?有点麻烦了
没法俯视整个分派的业务逻辑

Map+函数式接口

用上了Java8的新特性lambda表达式

判断条件放在key
对应的业务逻辑放在value
这样子写的好处是非常直观,能直接看到判断条件对应的业务逻辑

需求:根据优惠券(资源)类型 resourceType 和编码 resourceId 查询派发方式grantType

代码:

查询类

public class GrantTypeSerivce {
    public String redPaper(String resourceId){
        //红包的发放方式
        return "每周末9点发放";
    }
    public String shopping(String resourceId){
        //购物券的发放方式
        return "每周三9点发放";
    }
    public String QQVip(String resourceId){
        //qq会员的发放方式
        return "每周一0点开始秒杀";
    }
}
public class QueryGrantTypeService {
    private GrantTypeSerivce grantTypeSerivce = new GrantTypeSerivce();
    private Map<String, Function<String, String>> grantTypeMap = new HashMap<>();

    public void dispatcherInit() {
        grantTypeMap.put("红包", resourceId -> grantTypeSerivce.redPaper(resourceId));
        grantTypeMap.put("购物券", resourceId -> grantTypeSerivce.shopping(resourceId));
        grantTypeMap.put("qq会员", resourceId -> grantTypeSerivce.QQVip(resourceId));
    }

    public String getResult(String resourceType) {
        //Controller根据 优惠券类型resourceType、编码resourceId 去查询 发放方式grantType
        Function<String, String> result = grantTypeMap.get(resourceType);
        if (result != null) {
            //传入resourceId 执行这段表达式获得String型的grantType
            return result.apply(resourceType);
        }
        return "查询不到该优惠券的发放方式";
    }

主函数

public static void main(String[] args) {
        QueryGrantTypeService service = new QueryGrantTypeService();
        service.dispatcherInit();
        System.out.println(service.getResult("红包"));
    }
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐