我正在参与「启航方案」

忽如一夜春风来,千树万树梨花开。

咱们好,我是际遇,好久不见了,今天跟咱们聊聊工作中咱们经常会遇到的问题。

上货!

废话不多说,那咱们就直接开端吧。

小伙子,你在工作中用到过什么规划形式呢?

(这不又说到我心坎儿上来了吗?)面试官你好,是这样的,我在工作中用到过和触摸到过的规划形式有单例形式,工厂形式,代理形式,职责链形式,战略形式…balabala

那你跟我讲一讲战略形式吧

开闭准则

嗯好,是这样的,说到战略形式的话,咱们就不得不聊一聊七大软件架构规划准则之开闭准则了。望文生义呢,开闭准则是指一个软件实体,比方一个类或许说对象,它应该是对扩展敞开的,对修正关闭的。所谓开闭,也是对扩展和修正两个行为的一个准则。

简略来说呢,咱们的代码更新迭代的过程中,尽或许的不去修正咱们的源码,而是去增加咱们的新的代码。

战略形式的运用场景

战略形式会在什么场景下运用?

有大概三种运用场景吧

  • 针对同一类型问题,有多种处理方法,每一种都能独立解决问题。(例如在线付出的付出方法)
  • 需求自在切换算法的场景(例如汇率核算,不同场景下的税务核算)
  • 需求屏蔽算法规则的场景

那战略形式跟开闭准则有什么关系呢?

战略形式运用的便是面向对象的继承和多态机制,从而完结同一行为在不同的场景下具备不同的完结。也便是说咱们需求针对同一个行为,比方付款,在不同的场景,比方微信和付出宝这样不同的场景下,会有不同的完结方法。日常的情况下,咱们会经过if-else或许switch-case的句子来处理这样的逻辑。假如在不受开闭准则的约束下,咱们这样写是没有问题的。

那为什么不能够这样写?

(哟,鱼儿上钩了,这不就闭环了吗?)

咱们在规划代码的时分要遵从开闭准则,是因为在软件规划之初,或许主意不行完善,后续存在修正的或许性。那么在这样的条件下,仍是刚刚那个例子,在运用if-else或许switch-case就不遵从开闭准则,那后期需求是需求增加一种或许多种新的付出方法,比方银行卡付出,那咱们是不是又要在修正原来的代码文件?是不是需求增加if句子?或许说增加case句子?这就违反了开闭准则了。反而假如咱们一开端就运用战略形式来规划,就不需求修正源代码

那运用战略形式应该怎么做呢?

战略形式的具体完结

那仍是按照刚刚说的那个例子,要运用战略形式完结:

咱们首先需求定一个顶层接口付出方法。

public interface Payment {
  void pay(Pay pay);
}

当然还有咱们的付出对象的界说,以及其中的枚举界说:

public class Pay {
  private PaymentType paymentType;
​
  public PaymentType getPaymentType() {
    return paymentType;
   }
​
  public void setPaymentType(PaymentType paymentType) {
    this.paymentType = paymentType;
   }
}
​
public enum PaymentType {
  ALI_PAY("AliPayment"),
  WECHAT_PAY("WechatPaymet");
​
  private String type;
​
  private PaymentType(String type){
    this.setType(type);
   }
​
  private String getType() {
    return type;
   }
​
  private void setType(String type) {
    this.type = type;
   }
}

再界说两个不同的付出方法

@PaymentTypes(paymentType = PaymentType.ALI_PAY)
public class AliPayment implements Payment {
  @Override
  public void pay(Pay pay) {
    //ali payment logic.
   }
}
​
​
@PaymentTypes(paymentType = PaymentType.WECHAT_PAY)
public class WechatPayment implements Payment {
  @Override
  public void pay(Pay pay) {
    //we chat payment logic.
   }
}

一起咱们规划一个注解来表示当时付出方法是哪一种,以便于运用的时分判别:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
//注解标志付出方法
public @interface PaymentTypes {
  PaymentType paymentType();
}

那么关键在于遵从完结开闭准则,当需求新增一种付出方法的时分,怎么做到不修正源代码的情况下增加一种付出战略,那咱们就在界说一种工厂,此工厂能够提供一切的付出方法:

public class AutoRegisterPaymentFactory implements ApplicationContextAware, InitializingBean {
​
  private ApplicationContext applicationContext;
​
    //大局map,key为付出方法,value为付出方法对应的完结类
  static Map<PaymentType, Payment> paymentMap = new HashMap<>();
​
  static Map<String, Payment> beanMap = new HashMap<>();
    //获取当时付出方法的完结类
  public static Payment getPayment(PaymentType paymentType) throws Exception {
    if(paymentMap.containsKey(paymentType)) {
      return paymentMap.get(paymentType);
     } else {
      throw new Exception("this kind of payment not support");
     }
   }
​
  @Override
  public void afterPropertiesSet() throws Exception {
    //获取一切的付出方法完结类
    beanMap = applicationContext.getBeansOfType(Payment.class);
    List<Payment> paymentList = beanMap.values().stream().collect(Collectors.toList());
    if(!CollectionUtils.isEmpty(paymentList)) {
      for (Payment payment : paymentList) {
        //读取完结类的注解,获取完结类的类型
        if(payment.getClass().isAnnotationPresent(PaymentTypes.class)) {
          PaymentTypes paymentTypes = payment.getClass().getAnnotation(PaymentTypes.class);
          PaymentType paymentType = paymentTypes.paymentType();
            //加入大局map,完结付出方法主动注册
          paymentMap.put(paymentType,payment);
         }
       }
     }
   }
​
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
   }
}

一起,那调用形式就变成下面这样了:

public static void main(String args[]) throws Exception {
  Pay aliPay = new Pay();
  aliPay.setPaymentType(PaymentType.ALI_PAY);
    //主动注册
  AutoRegisterPaymentFactory autoRegisterPaymentFactory = new AutoRegisterPaymentFactory();
    //获取当时付出类型下的完结类
  Payment payment = AutoRegisterPaymentFactory.getPayment(aliPay.getPaymentType());
    //调用完结类的事务逻辑
  payment.pay(aliPay);
}

那经过上面的战略形式的完结方法呢,假如要增加一个付出方法,咱们只需求新增一个完结类,并加上付出方法的注解,并在原有的枚举类中增加这个新的付出方法枚举就能够了,无需修正原有的任何事务逻辑。

对了,在主动注册这个工厂中,也能够经过反射的方法去获取完结类。

那面试官,我讲完了,您有什么疑问吗?(反将一军!)

那运用战略形式有什么缺陷呢?

(就离谱!这也能问出来?)

咳咳…是这样的,经过咱们刚刚的谈天您也看到了,我水没喝两口,嘴上可没停啊。这便是战略形式的第一个缺陷:代码中会产生相当多的战略类,增加维护的难度。也便是说,每一种完结方法都会有一个对应的完结类,而差异与非战略形式的代码,一个service中能够将不同的战略写为不同的方法,可是战略形式下就显得完结类特别多,当时提交文件也多,code review要看的也多……

一起呢,咱们客户端有必要知道一切的战略,而且自行决定运用哪一个战略类。以上呢,便是我觉得或许存在的一些不足。可是双刃剑嘛,咱们得到了遵从开闭准则的代码,降低了if-else的或许的圈复杂度,一起,还能够进步算法的保密性和安全性,运用哪个?自己衡量衡量吧。

看来你根底还不错,接下来再聊聊…

跋文

写这个文章呢,是因为我在实际工作中确实遇到了不少相似的场景,后期需求修正原需求的需求也见的不少,所以想着爽性就写下来吧。

今后我会持续把其他的规划形式也持续更下去

假如有帮到你,万分侥幸!