专栏名称: Java基基
一个苦练基本功的 Java 公众号,所以取名 Java 基基
目录
相关文章推荐
传媒1号  ·  非遗传承的Z世代方程式 ·  11 小时前  
传媒1号  ·  非遗传承的Z世代方程式 ·  11 小时前  
北京应急  ·  利用AI炮制灾害谣言?依法查处! ·  15 小时前  
北京应急  ·  利用AI炮制灾害谣言?依法查处! ·  15 小时前  
财联社AI daily  ·  DeepSeek,大降价! ·  2 天前  
财联社AI daily  ·  DeepSeek,大降价! ·  2 天前  
智在点滴  ·  清华大学 DeepSeek ... ·  2 天前  
智在点滴  ·  清华大学 DeepSeek ... ·  2 天前  
果粉之家  ·  DeepSeek预测:iPhone ... ·  2 天前  
果粉之家  ·  DeepSeek预测:iPhone ... ·  2 天前  
51好读  ›  专栏  ›  Java基基

工厂+策略模式的妙用~

Java基基  · 公众号  · 科技自媒体  · 2024-12-26 11:55

正文

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入 芋道快速开发平台 知识星球。 下面是星球提供的部分资料:

👉 这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本

来源:juejin.cn/post/
7375386125798506522


Hello,大家好。大家工作中有没有在一个业务使用过大量的if else啊,如果有,你看到这篇文章就能去优化自己的代码了,兄弟们赶紧冲啊!!!

在这篇文章中,我将使用会员制度打折促销为案例,为大家循序渐进的讲述使用工厂+策略模式之优雅。

Java版本: 21

SpringBoot版本: 3.3.0

案例前置

Demo 中没有使用数据库相关内容,而是使用缓存热启动提前在 Map 中加载了些参数,存在用户(id, name, gender, age, type)与商品(id, name, price)实体,之前我写过关于缓存热启动的文章,大家有兴趣可以去看看,这里放个具体的缓存代码,在这个 Demo 中,缓存热启动也做了优化,我会把源码 github 链接放在下边,大家也可以进行参阅:

用户缓存
@Slf4j
@Component
public class UserCache extends AbstractCache<LongUser{

    private final Map userMap = new HashMap<>();

    /**
     * 初始化缓存
     */

    @Override
    public void init() {
        this.userMap.put(1L, User.builder().id(1L).name("Lucy").gender("女").age(18).type(0).build());
        this.userMap.put(2L, User.builder().id(2L).name("Jack").gender("男").age(20).type(1).build());
        this.userMap.put(3L, User.builder().id(3L).name("Mick").gender("男").age(22).type(2).build());
        this.userMap.put(4L, User.builder().id(4L).name("Andy").gender("女").age(23).type(3).build());
        this.userMap.put(5L, User.builder().id(5L).name("Pelin").gender("女").age(23).type(4).build());
        log.info("user cache init success: {}", JSON.toJSONString(this.userMap));
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */

    @Override
    public User get(Long key) {
        return this.userMap.get(key);
    }

    /**
     * 清空缓存
     */

    @Override
    public void clear() {
        this.userMap.clear();
    }
}
商品缓存
@Slf4j
@Component
public class MerchandiseCache extends AbstractCache<LongMerchandise{

    private final Map merchandiseMap = new HashMap<>();

    @Override
    public void init() {
        this.merchandiseMap.put(1L, Merchandise.builder().id(1L).name("小米14").price(BigDecimal.valueOf(3999.00)).build());
        this.merchandiseMap.put(2L, Merchandise.builder().id(2L).name("小米路由器").price(BigDecimal.valueOf(399.00)).build());
        this.merchandiseMap.put(3L, Merchandise.builder().id(3L).name("小米扫地机器人").price(BigDecimal.valueOf(2299.00)).build());
        this.merchandiseMap.put(4L, Merchandise.builder().id(4L).name("小米电视S75英寸4K").price(BigDecimal.valueOf(3899.00)).build());
        this.merchandiseMap.put(5L, Merchandise.builder().id(5L).name("小米SoundPro").price(BigDecimal.valueOf(899)).build());
        log.info("merchandise cache init success: {}", JSON.toJSONString(this.merchandiseMap));
    }

    @Override
    public Merchandise get(Long key) {
        return this.merchandiseMap.get(key);
    }

    @Override
    public void clear() {
        this.merchandiseMap.clear();
    }
}
折扣枚举
@Getter
public enum DiscountEnum {

    NORMAL(0"普通用户", BigDecimal.valueOf(1.00)),
    VIP(1"普通VIP", BigDecimal.valueOf(0.95)),
    PVIP(2"高级VIP", BigDecimal.valueOf(0.90)),
    EVIP(4"至尊VIP", BigDecimal.valueOf(0.85)),
    SVIP(3"超级VIP", BigDecimal.valueOf(0.80));

    /**
     * 用户类型code
     */

    private final int code;
    /**
     * 用户类型文字
     */

    private final String type;
    /**
     * 折扣
     */

    private final BigDecimal discount;

    DiscountEnum(int code, String type, BigDecimal discount) {
        this.code = code;
        this.type = type;
        this.discount = discount;
    }

    /**
     * 根据code获取枚举
     *
     * @param code code
     * @return DiscountEnum
     */

    public static DiscountEnum valueOf(int code) {
        for (DiscountEnum value : DiscountEnum.values()) {
            if (value.getCode() == code) {
                return value;
            }
        }
        return null;
    }

}

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

大量 if else 的弊端

大家看,下边这段根据用户类型(type)进行打折促销的代码:

@Slf4j
@Service("shoppingService1")
public class ShoppingService1Impl implements ShoppingService {

    @Resource
    private UserCache userCache;

    @Resource
    private MerchandiseCache merchandiseCache;

    /**
     * 购买商品
     *
     * @param userId        用户ID
     * @param merchandiseId 商品ID
     * @return 购买结果
     */

    @Override
    public Map buy(Long userId, Long merchandiseId) {
        User user = userCache.get(userId);
        Merchandise merchandise = merchandiseCache.get(merchandiseId);
        log.info("current user: {}", JSON.toJSONString(user));
        log.info("current merchandise: {}", JSON.toJSONString(merchandise));
        DecimalFormat df = new DecimalFormat("0.00");
        DiscountEnum discountEnum = DiscountEnum.valueOf(user.getType());
        log.info("user type: {}, 享受折扣: {}", discountEnum.getType(), df.format(discountEnum.getDiscount()));
        // 普通用户
        if (user.getType() == 0) {
            return Map.of("用户姓名", user.getName(),
                    "商品名称", merchandise.getName(),
                    "商品价格", merchandise.getPrice(),
                    "折后价格", merchandise.getPrice().multiply(discountEnum.getDiscount()));

        } else
            // 普通VIP
            if (user.getType() == 1) {
                return Map.of("用户姓名", user.getName(),
                        "用户性别", user.getGender(),
                        "用户等级", discountEnum.getType(),
                        "商品名称", merchandise.getName(),
                        "商品价格", merchandise.getPrice(),
                        "应享折扣", discountEnum.getDiscount(),
                        "折后价格", merchandise.getPrice().multiply(discountEnum.getDiscount()));
            } else
                // 高级VIP
                if (user.getType() == 2) {
                    return Map.of(
                            "用户姓名", user.getName(),
                            "用户年龄", user.getAge(),
                            "用户等级", discountEnum.getType(),
                            "商品名称", merchandise.getName(),
                            "商品价格", merchandise.getPrice(),
                            "应享折扣", discountEnum.getDiscount(),
                            "折后价格", merchandise.getPrice().multiply(discountEnum.getDiscount()));
                } else
                    // 至尊VIP
                    if (user.getType() == 3) {
                        return Map.of("用户姓名", user.getName(),
                                "用户性别", user.getGender(),
                                "用户年龄", user.getAge(),
                                "用户等级", discountEnum.getType(),
                                "商品名称", merchandise.getName(),
                                "商品价格", merchandise.getPrice(),
                                "应享折扣", discountEnum.getDiscount(),
                                "折后价格", merchandise.getPrice().multiply(discountEnum.getDiscount()));
                    } else
                        // 超级VIP
                        if (user.getType() == 4) {
                            return Map.of("用户ID", user.getId(),
                                    "用户姓名", user.getName(),
                                    "用户性别", user.getGender(),
                                    "用户年龄", user.getAge(),
                                    "用户等级", discountEnum.getType(),
                                    "商品名称", merchandise.getName(),
                                    "商品价格", merchandise.getPrice(),
                                    "应享折扣", discountEnum.getDiscount(),
                                    "折后价格", merchandise.getPrice().multiply(discountEnum.getDiscount()));
                        }
        return Map.of();
    }

}

其实问题很容易看出来,就是这样的代码太臃肿了,也违反了开闭原则(对扩展开放,对修改关闭),将来又加入新的用户类型,还需要在这基础上继续添加逻辑代码,实在是太不优雅了。

为了解决这个问题呢,先是引入了策略模式,其实单纯的策略模式还是不能够解决全部问题,大家继续往下看。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

策略模式

先定义一个策略接口:

public interface ShoppingStrategyAware {
    /**
     * 购买
     *
     * @param user        用户
     * @param merchandise 商品
     * @return 结果
     */

    Map buy(User user, Merchandise merchandise);
}

再将不同的用户类型当做不同的策略分别创建一个类来继承这个策略接口,案例中一共有五个用户类型,全部代码放上来就太多啦(普通用户、普通VIP、高级VIP、至尊VIP、超级VIP),这里放两个进行举例,完整的请查看下边的 github 源码链接。

普通用户

@Slf4j
@Component
public class NormalStrategy implements ShoppingStrategyAware {

    @Override
    public Map buy(User user, Merchandise merchandise) {
        log.info("current strategy: {}", getStrategy().getType());
        DecimalFormat df = new DecimalFormat("0.00");
        Map result = new HashMap<>();
        result.put("用户姓名", user.getName());
        result.put("商品名称", merchandise.getName());
        result.put("商品原价", df.format(merchandise.getPrice()));
        result.put("折后价格", df.format(merchandise.getPrice().multiply(getStrategy().getDiscount())));
        return result;
    }
    
}

普通VIP

@Slf4j
@Component
public class VIPStrategy implements ShoppingStrategyAware {

    @Override
    public Map buy(User user, Merchandise merchandise) {
        log.info("current strategy: {}", getStrategy().getType());
        DecimalFormat df = new DecimalFormat("0.00");
        Map result = new HashMap<>();
        result.put("用户姓名", user.getName());
        result.put("用户性别", user.getGender());
        result.put("用户等级", getStrategy().getType());
        result.put("商品名称", merchandise.getName());
        result.put("商品原价", df.format(merchandise.getPrice()));
        result.put("应享折扣", df.format(getStrategy().getDiscount()));
        result.put("折后价格", df.format(merchandise.getPrice().multiply(getStrategy().getDiscount())));
        return result;
    }
    
}

将上边大量 if else 进行改造,将不同的策略在业务中进行注入:

@Slf4j
@Service("shoppingService2")
public class ShoppingService2Impl implements ShoppingService {

    @Resource
    private UserCache userCache;

    @Resource
    private MerchandiseCache merchandiseCache;

    @Resource(name = "normalStrategy")
    private ShoppingStrategyAware normalStrategy;

    @Resource(name = "VIPStrategy")
    private ShoppingStrategyAware vipStrategy;

    @Resource(name = "PVIPStrategy")
    private ShoppingStrategyAware pvipStrategy;

    @Resource(name = "EVIPStrategy")
    private ShoppingStrategyAware evipStrategy;

    @Resource(name = "SVIPStrategy")
    private ShoppingStrategyAware svipStrategy;

    /**
     * 购买商品
     *
     * @param userId        用户ID
     * @param merchandiseId 商品ID
     * @return 购买结果
     */

    @Override
    public Map buy(Long userId, Long merchandiseId) {
        User user = userCache.get(userId);
        Merchandise merchandise = merchandiseCache.get(merchandiseId);
        log.info("current user: {}", JSON.toJSONString(user));
        log.info("current merchandise: {}", JSON.toJSONString(merchandise));
        // 普通用户
        if (user.getType() == 0) {
            return normalStrategy.buy(user, merchandise);
        } else
            // 普通VIP
            if (user.getType() == 1) {
                return vipStrategy.buy(user, merchandise);
            } else
                // 高级VIP
                if (user.getType() == 2) {
                    return pvipStrategy.buy(user, merchandise);
                } else
                    // 至尊VIP
                    if (user.getType() == 3) {
                        return evipStrategy.buy(user, merchandise);
                    } else
                        // 超级VIP
                        if (user.getType() == 4) {
                            return svipStrategy.buy(user, merchandise);






请到「今天看啥」查看全文


推荐文章
传媒1号  ·  非遗传承的Z世代方程式
11 小时前
传媒1号  ·  非遗传承的Z世代方程式
11 小时前
北京应急  ·  利用AI炮制灾害谣言?依法查处!
15 小时前
北京应急  ·  利用AI炮制灾害谣言?依法查处!
15 小时前
财联社AI daily  ·  DeepSeek,大降价!
2 天前
财联社AI daily  ·  DeepSeek,大降价!
2 天前
金融先生MrFinance  ·  毕业后想去银行工作?先听我说说
8 年前
译言  ·  影史经典十部世界末日电影
7 年前