Springboot 优雅处理 if/else

本贴最后更新于 405 天前,其中的信息可能已经时异事殊

策略模式

策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装成独立的类,使得它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端。

策略模式由三个部分组成:策略接口、具体策略类和环境类。策略接口定义了所有具体策略类都需要实现的方法;具体策略类实现了策略接口,并提供不同的算法实现;环境类持有一个策略对象,并利用策略对象执行具体的算法。

策略模式适用情况

  • 需要根据不同的条件或参数选择不同的算法或策略。例如,在一个电商网站中,根据用户的购买历史、地理位置等信息,选择不同的优惠策略或物流配送策略。

  • 需要封装一组相似的算法,以便在不同的情况下使用不同的算法。例如,在一个游戏中,根据不同的敌人类型和玩家状态选择不同的战斗策略。

  • 有一组类似的行为,但是可能会发生变化。通过将每个行为封装为一个具体策略类,可以更灵活地添加、删除或修改行为。例如,在一个日程管理应用中,可以根据用户的不同需求设置不同的提醒策略。

  • 不希望暴露复杂的条件语句或逻辑判断代码。使用策略模式可以将条件语句封装在具体策略类中,使得代码更加清晰、易于理解和维护。

总的来说,策略模式适用于需要根据不同的条件选择不同算法或策略,并且希望将算法或策略的实现与使用代码解耦的情况。它能够提供灵活性、扩展性和可维护性,使得代码更具可读性和可重用性。

工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的接口,但具体的对象创建过程由子类或子工厂类来决定。通过使用工厂模式,可以将对象的创建和使用解耦,使得代码更加灵活、可扩展和易于维护。

工厂模式适用情况

  • 当需要创建复杂对象或对象之间存在复杂的依赖关系时,可以使用工厂模式来封装对象的创建过程,并隐藏创建细节。
  • 当需要创建的对象可能会随着程序的运行时条件变化而变化时,可以使用工厂模式来动态地选择合适的对象创建方式。
  • 当希望将对象的创建逻辑集中在一处,以便于维护和管理时,可以使用工厂模式来统一对象的创建过程。

策略 + 工厂模式优化

一、利用 Spring 自动注入

支付接口 IPay 和具体实现类 AliPayWeiXinPayYiBaoPay

public interface IPay {
    String pay();
}

@Service
@Slf4j
public class AliPay implements IPay {

    @Override
    public String pay() {
        log.info("===调用支付宝接口发起支付业务===");
        return "调用支付宝接口发起支付业务";
    }
}

@Service
@Slf4j
public class WeiXinPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用微信接口发起支付业务===");
        return "调用微信接口发起支付业务";
    }
}

@Service
@Slf4j
public class YiBaoPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用易宝接口发起支付业务===");
        return "调用易宝接口发起支付业务";
    }
}

工厂类 PayFactory

@Component
public class PayFactory {
    /**
     * 利用Spring自动注入的特点。Key为bean的名称、value为IPay接口对应的bean实现类
     */
    @Autowired
    Map<String, IPay> map;

    public  IPay getInstance(String type) {
        return map.get(type);
    }
}

工厂类使用

@RestController
public class PayController {

    @Autowired
    private PayFactory payFactory;

    @GetMapping("/alipay")
    public String aliPay(String type) {
        IPay pay = payFactory.getInstance(type);
        return pay.pay();
    }
}

Key 为 bean 的名称、value 为 IPay 接口对应的 bean 实现类。对应的业务类型不好扩展

二、继承 InitializingBean+ 静态工厂方法调用处理

继承 InitializingBean,重写 afterPropertiesSet()

public interface IPay extends InitializingBean {
    String pay();
}


@Service
@Slf4j
public class AliPay implements IPay {

    @Override
    public String pay() {
        log.info("===调用支付宝接口发起支付业务===");
        return "调用支付宝接口发起支付业务";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        PayFactory.registerHandler("ali", this);
    }
}

@Service
@Slf4j
public class WeiXinPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用微信接口发起支付业务===");
        return "调用微信接口发起支付业务";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        PayFactory.registerHandler("weixin", this);
    }
}

@Service
@Slf4j
public class YiBaoPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用易宝接口发起支付业务===");
        return "调用易宝接口发起支付业务";
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        PayFactory.registerHandler("yibao", this);
    }
}

三、注解 +CommandLineRunner+ApplicationContextAware 处理/ApplicationListener< ContextRefreshedEvent >

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PayCode {
    String value();
    String name();
}

public interface IPay {
    String pay();
}

@PayCode(value = "alia", name = "支付宝支付")
@Service
@Slf4j
public class AliPay implements IPay {

    @Override
    public String pay() {
        log.info("===调用支付宝接口发起支付业务===");
        return "调用支付宝接口发起支付业务";
    }
}

@PayCode(value = "weixin", name = "微信支付")
@Service
@Slf4j
public class WeiXinPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用微信接口发起支付业务===");
        return "调用微信接口发起支付业务";
    }
}

@PayCode(value = "yibao", name = "易宝支付")
@Service
@Slf4j
public class YiBaoPay implements IPay {
    @Override
    public String pay() {
        log.info("===调用易宝接口发起支付业务===");
        return "调用易宝接口发起支付业务";
    }
}

第一种方式

工厂类 PayFactory 实现 Spring 生命周期函数在 run 方法中处理被标记的 @PayCode 的 bean 对象加入到静态容器 Map 中:

CommandLineRunner:当前 Bean 被 IOC 容器装配完成调用 run()方法

ApplicationContextAware: 获取容器对象 ApplicationContext

@Component
public class PayFactory implements CommandLineRunner, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private static final Map<String, IPay> PAY_MAP = new HashMap<>();

    /**
     * 获取对应实例Bean
     * @param type
     * @return
     */
    public static IPay getInstance(String type) {
        return PAY_MAP.get(type);
    }


    @Override
    public void run(String... args) throws Exception {
        Map<String, Object> beansWithAnnotation = 
				applicationContext.getBeansWithAnnotation(PayCode.class);
        if (beansWithAnnotation !=null ) {
            beansWithAnnotation.forEach((k,v)->{
                PayCode payCodeAnn = v.getClass().getAnnotation(PayCode.class);
                PAY_MAP.put(payCodeAnn.value(), (IPay)v);
            });
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

第二种方式

实现 ApplicationListener<ContextRefreshedEvent>监听事件接口 代替 CommandLineRunner, ApplicationContextAware

ContextRefreshedEvent事件: ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh()方法来发生。此处的初始化是指:所有的 Bean 被成功装载,后处理 Bean 被检测并激活,所有 Singleton Bean 被预实例化,ApplicationContext 容器已就绪可用。

@Component
public class PayFactory implements ApplicationListener<ContextRefreshedEvent> {

    private static final Map<String, IPay> PAY_MAP = new HashMap<>();

    /**
     * 获取对应实例Bean
     * @param type
     * @return
     */
    public static IPay getInstance(String type) {
        return PAY_MAP.get(type);
    }
  
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PayCode.class);
        if (beansWithAnnotation !=null ) {
            beansWithAnnotation.forEach((k,v)->{
                PayCode payCodeAnn = v.getClass().getAnnotation(PayCode.class);
                PAY_MAP.put(payCodeAnn.value(), (IPay)v);
            });
        }
    }
}

使用

@RestController
public class PayController {
  
    @GetMapping("/alipay")
    public String aliPay(String type) {
        IPay pay = PayFactory.getInstance(type);
        return pay.pay();
    }
}

Map+ 函数式接口代替策略模式

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

  • 判断条件放在 key 中
  • 对应的业务逻辑放在 value 中

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

@Service
public class QueryGrantTypeService {

    @Autowired
    private GrantTypeService grantTypeService;

    private Map<String, Function<String, String>> grantTypeMap = new HashMap();

    /**
     * 初始化业务分派逻辑,代替了if-else部分
     * key: 优惠券类型
     * value: lambda表达式,最终会获得该优惠券的发放方式
     */
    @PostConstruct
    public void dispatcherInit() {
        grantTypeMap.put("红包", resourceId -> grantTypeService.redPaper(resourceId));
        grantTypeMap.put("购物券", resourceId -> grantTypeService.shopping(resourceId));
        grantTypeMap.put("qq会员", resourceId -> grantTypeService.QQVip(resourceId));
    }


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

如果单个 if 语句块的业务逻辑有很多行的话,我们可以把这些 业务操作抽出来,写成一个单独的 Service,即:

@Service
public class GrantTypeService {

    public String redPaper(String resourceId){
        //红包的发放方式
        return "每周末9点发放";
    }
    public String shopping(String resourceId){
        //购物券的发放方式
        return "每周三9点发放";
    }
    public String QQVip(String resourceId){
        //qq会员的发放方式
        return "每周一0点开始秒杀";
    }
}

使用

@RestController
public class GrantTypeController {

    @Autowired
    private QueryGrantTypeService queryGrantTypeService;

    @GetMapping("/grantType/{resourceType}/{resourceId}")
    public String test(@PathVariable String resourceType, @PathVariable String resourceId){
        return queryGrantTypeService.getResult(resourceType, resourceId);
    }
}

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1459 回帖 • 34 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...