springboot 通过设计模式来消除你代码中的if else/switch-case

最近在学习spring源码,发现里面的设计模式的应用对工作中写出高质量的代码很有帮助,现在通过一个例子来说明怎么消除代码中的if else 来达到高质量的代码,顺便可以装一下(__) 嘻嘻……

需求:
现在有三种支付方式 微信支付(WECHAT_PAY),谷歌支付(GOOGLE_PAY), 支付宝支付(ALIPAY),现在根据客户端传入的方式来选择对应的支付.

现在来展示常规写法(小白写法)

/**支付接口*/
public interface Payment {
    void pay();
}
/**各种支付*/
@Slf4j
@Component
public class Alipay implements Payment {
    @Override
    public void pay() {
        log.info("alipay 支付完成");
    }
}

@Slf4j
@Component
public class GooglePay implements Payment {
    @Override
    public void pay() {
        log.info("Google Pay 支付完成");
    }
}

@Slf4j
@Component
public class WechantPay implements Payment {
    @Override
    public void pay() {
        log.info("Wechat Pay 支付完成");
    }
}

public enum PaymentMethod {
    /**支付枚举*/
    WECHAT_PAY, GOOGLE_PAY, ALIPAY;
}

@Service
public class PaymentService {
    @Autowired
    private Alipay alipay;
    @Autowired
    private GooglePay googlePay;
    @Autowired
    private WechantPay wechantPay;
    public void pay(PaymentMethod paymentMethod) {
        switch (paymentMethod) {
            case ALIPAY:
                alipay.pay();
                break;
            case GOOGLE_PAY:
                googlePay.pay();
                break;
            case WECHAT_PAY:
                wechantPay.pay();
                break;
            default:
                throw new RuntimeException("不支持该种支付方式");
        }
    }
}

客户端支付

@SpringBootTest
class PaymentTest {
    @Autowired
    private PaymentService paymentService;
    @Test
    void paymentClient() {
        // 支付方式
        paymentService.pay(PaymentMethod.ALIPAY);
    }

}

支付结果:
支付结果
上面代码有一个支付接口,三种支付方式,支付路由为PaymentService中的switch方法(这里和if else一样的意思), 我们可以看到paymentService引入了三种支付的实现类,假如我们现在引入了银联支付,那么我们的paymentService也需要跟着进行修改.怎么解决这个问题,很多人想到了策略模式,每个支付对应一个策略,然后在去实现,策略模式的实现,我这里就不在进行展开了,网上有很多教程.现在来介绍我的实现.

注解方式实现

我们的目的是写一个通用的实现来解决类似的问题,不只是支付方式的问题,所有switch-case的问题基本都可以用这中方式来解决,先看代码实现.

这里用到了模板方法模式,这个类主要是提供applicationContext来操作spring容器的Bean通用方法

public abstract class AbstractSparrowContext implements ApplicationContextAware, InitializingBean {
   protected ApplicationContext applicationContext;

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

   /**
    * <p> Title: getBean
    * <p> Description: 获取spring容器中的对象
    *
    * @param targetClz 类
    *
    * @return T
    *
    * @author yousuf 2020/2/22
    *
    */
   @SuppressWarnings("unchecked")
   protected <T> T getBean(Class<T> targetClz) {
       T beanInstance = null;
       //byType
       try {
           beanInstance = applicationContext.getBean(targetClz);
       } catch (Exception ignored) {

       }
       //byName
       if (beanInstance == null) {
           String simpleName = targetClz.getSimpleName();
           simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
           beanInstance = (T) applicationContext.getBean(simpleName);
       }
       return beanInstance;
   }

   /**
    * <p> Title: getBeanMapByAnnotation
    * <p> Description: 获取注解的类
    *
    * @param annotationClz 注解
    *
    * @return java.util.Map<java.lang.String,java.lang.Object>
    *
    * @author zhangshuai 2019/11/6
    *
    */
   protected Map<String, Object> getBeanMapByAnnotation(Class<? extends Annotation> annotationClz) {
       return applicationContext.getBeansWithAnnotation(annotationClz);
   }
}

继续抽象一个类出来根据注解来封装需要的Bean.

public abstract class AbstractSparrowAnnotationBeanMap<A extends Annotation,B> extends AbstractSparrowContext {
    /**
     * <p> Title: getAnnotation
     * <p> Description: 需要缓存的注解类型
     *
     * @return java.lang.Class<A>
     *
     * @author yousuf 2020/2/24
     *
     */
    public abstract Class<A> getAnnotation();
    /**
     * <p> Title: refresh
     * <p> Description: 暴露给实现类去操作刷新earlyBeans的接口
     *
     * @author yousuf 2020/2/24
     *
     */
    public abstract void refresh(Map<A, B> annotationBeanMap);

    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
    	Map<A, B> annotationBeanMap = Maps.newHashMap();
        Map<String, Object> beanMap = getBeanMapByAnnotation(getAnnotation());
        beanMap.values().forEach(bean -> {
            A annotation = (A) AnnotationUtils.findAnnotation(value.getClass(), getAnnotation());
            annotationBeanMap.put(annotation, (B) bean);
        });
        refresh(annotationBeanMap);
    }
}

解释一下上面代码的用途 :

  • getAnnotation()获取注解,这个有具体的实现类来提供.
  • refresh()用于实现类操纵annotationBeanMap缓存,获取符合自己需求的Bean
  • afterPropertiesSet() spring框架的方法,用于初始化数据.这里根据注解找到对应的Bean,并缓存到annotationBeanMap, key值为注解类, value为Bean

到此框架层面的代码已经写完, 这个方法可以用来解决这一类问题,现在来展示通过上面的模板方法来解决文章开头提到的需求.

  • 新增一个注解
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pay {
   PaymentMethod method();
}
  • 在各种支付方式上加上该注解
@Slf4j
@Component
@Pay(method = PaymentMethod.ALIPAY)
public class Alipay implements Payment {
   @Override
   public void pay() {
       log.info("alipay 支付完成");
   }
}
  • 新增一个子类来实现AbstractSparrowAnnotationBeanMap
@Component
public class PaymentHandler extends AbstractSparrowAnnotationBeanMap<Pay, Payment> {
   private static final Map<PaymentMethod, Payment> PAY_MAP = Maps.newHashMap();
   @Override
   public Class<Pay> getAnnotation() {
       return Pay.class;
   }

   @Override
   public void refresh(Map<Pay, Payment> annotationBeanMap) {
       annotationBeanMap.forEach((pay, payment) -> PAY_MAP.put(pay.method(), payment));
   }

   public static void pay(PaymentMethod method) {
       Payment payment = PAY_MAP.get(method);
       payment.pay();
   }
}

到此所有代码写完,我们来测试一下我们的代码是否正确,在PaymentService中新增一个方法

    public void annotationPay(PaymentMethod paymentMethod) {
        PaymentHandler.pay(paymentMethod);
    }

测试结果
测试结果

总结:
通过模板方法模式归纳了一类问题的通用解决办法,去除了switch-case,最主要的是当有新的支付方式接入的时候只需要新增该支付方式的实现类,而不需要修改支付服务(paymentService),这样正是***对修改关闭,对扩展开放***的最好体现.

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计模式,可以使用策略模式来优化使用switch case语句。策略模式允许我们定义一系列的算法,并将其封装成独立的类,使得算法可以相互替换,而不会影响到客户端的代码。 下面是一个使用策略模式来优化switch case的示例: 1. 定义一个策略接口,例如`IStrategy`,并在接口声明一个方法,例如`execute()`。 2. 创建实现策略接口的具体策略类,例如`StrategyA`、`StrategyB`等,并实现`execute()`方法。 3. 在客户端代码,使用一个变量来存储当前的策略对象,并在需要执行算法的地方调用该对象的`execute()`方法。 这样,每个具体的策略类代表了一个算法,而客户端则可以根据需要动态地切换不同的策略对象,避免了使用switch case语句。 以下是一个示例代码: ```java // 定义策略接口 interface IStrategy { void execute(); } // 具体策略类A class StrategyA implements IStrategy { @Override public void execute() { // 实现具体的算法逻辑 System.out.println("执行策略A"); } } // 具体策略类B class StrategyB implements IStrategy { @Override public void execute() { // 实现具体的算法逻辑 System.out.println("执行策略B"); } } // 客户端代码 public class Client { public static void main(String[] args) { // 创建策略对象 IStrategy strategy = new StrategyA(); // 执行算法 strategy.execute(); // 切换策略对象 strategy = new StrategyB(); // 执行算法 strategy.execute(); } } ``` 通过使用策略模式,我们可以将每个具体的算法封装成独立的类,使得代码更加清晰、可扩展和可维护。同时,客户端可以动态地切换算法,而无需修改大量的if-else或switch case语句。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值