spring解决开闭原则的if else

  • Post author:
  • Post category:其他




需求:

  • 完成支付模块需要支持微信支付,支付宝支付,通过传入code区分不同的支付方式,完成不同的支付逻辑
  • 我们首先想到的是用if 判断支付方式 :
if(payType.getCode == 101){
	// 支付宝支付
} else if(payType.getCode == 102){
	// 微信支付逻辑
}

但是如果我们需要增加一个银联支付,则需要继续改动业务逻辑,添加更多的if else,这样明显不符合设计原则中的开闭原则:对扩展开放,对修改关闭

所以产生了如下的解决方式:

  • PayCode.java 支付方式自定义注解
@Retention(RetentionPolicy.RUNTIME)   // 被修饰的注解存在源码、字节码、内存(运行时)
@Target(ElementType.TYPE)   //  修饰类或接口
public @interface PayCode {
    int value();
    String name();
}

对注解类上的注解有疑问的,点这个:

https://blog.csdn.net/qq_45752401/article/details/111653365

  • Pay.java 支付接口
public interface Pay {
    /**
     * pay
     */
    void pay();
}
  • AliPay.java 支付宝支付业务逻辑
@PayCode(value = 101, name = "支付宝")
public class AliPay implements Pay{
    @Override
    public void pay() {
        System.out.println("执行支付宝支付逻辑...");
    }
}
  • WechatPay.java 微信支付业务逻辑
@PayCode(value = 102, name = "微信")
public class WechatPay implements Pay{
    @Override
    public void pay() {
        System.out.println("执行微信支付逻辑...");
    }
}
  • PayConfiguration.java 让Spring扫描到注解标记的类
@Configuration  // 被@configuration修饰,为配置类
@ComponentScan(includeFilters = {   // 扫描本身及延伸类
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = PayCode.class)     // 扫描被@PayCode注解修饰的类
})
public class PayConfiguration {
}
  • MyqxinService.java 监听器,从spring容器得到被PayCode注解标记类的对象,并通过传入的code调用调用不同的pay方法
@Service
public class MyqxinService implements ApplicationListener<ContextRefreshedEvent> {  // 监听spring容器

    private Map<Integer,Pay> payMap = null;     // if else

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 获取上下文对象
        ApplicationContext applicationContext = event.getApplicationContext();
        // 获取被PayCode注解修饰的实例对象  Map<String,Object> String: 默认key ;Object 实例对象
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(PayCode.class);
        // ConcurrentHashMap线程安全的HashMap   使用场景,Map的成员变量推荐使用。而如方法内的局部变量则推荐使用 HashMap
        payMap = new ConcurrentHashMap<>();
        beans.forEach((key,value) -> {
            // key spring默认命令方式的名称 ;value 实例对象
            // getAnnotation获取该类上的@PayCode注解,并获注解上的value值
            Integer type = value.getClass().getAnnotation(PayCode.class).value();
            // 保存map
            payMap.put(type,(Pay) value);
        });
    }

    /**
     * 实例调用
     * @param code
     */
    public void pay(Integer code){
        payMap.get(code).pay();
    }
}
  • DemoTest.java 测试类
public class DemoTest {
    public static void main(String[] args) {
        // 初始化spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PayConfiguration.class);
        // 获取实例对象
        MyqxinService myqxinService = applicationContext.getBean("myqxinService", MyqxinService.class);
        // 调用
        myqxinService.pay(101);
        myqxinService.pay(102);
    }
}
  • 结果

    在这里插入图片描述


注意:

红框这里是spring初始化后,容器里面的实例对象名称,我们在获取实例对象的时候,如果名称写错了就会抛出异常

  • DemoTest.java 测试类
public class DemoTest {
    public static void main(String[] args) {
        // 初始化spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PayConfiguration.class);
        // 获取实例对象
        MyqxinService myqxinService = applicationContext.getBean("myqxinservice", MyqxinService.class);
        // 调用
        myqxinService.pay(101);
        myqxinService.pay(102);
    }
}

异常如下:

在这里插入图片描述

  • 这样,我们即便在将来的业务中需要添加银联支付,也不需要改动原有代码,新增一个银联支付的类即可
  • UnionPay.java 银联支付业务逻辑
@PayCode(value = 103,name = "银联")
public class UnionPay implements Pay {
    @Override
    public void pay() {
        System.out.println("执行银联支付逻辑。。。");
    }
}
  • DemoTest.java 测试类
public class DemoTest {
    public static void main(String[] args) {
        // 初始化spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PayConfiguration.class);
        // 获取实例对象
        MyqxinService myqxinService = applicationContext.getBean("myqxinService", MyqxinService.class);
        // 调用
        myqxinService.pay(101);
        myqxinService.pay(102);
        myqxinService.pay(103);
    }
}
  • 结果

    在这里插入图片描述