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<Long, User> {
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<Long, Merchandise> {
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/
大家看,下边这段根据用户类型(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;
}
}
@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);