我正在参与「启航方案」
忽如一夜春风来,千树万树梨花开。
咱们好,我是际遇,好久不见了,今天跟咱们聊聊工作中咱们经常会遇到的问题。
上货!
废话不多说,那咱们就直接开端吧。
小伙子,你在工作中用到过什么规划形式呢?
(这不又说到我心坎儿上来了吗?)面试官你好,是这样的,我在工作中用到过和触摸到过的规划形式有单例形式,工厂形式,代理形式,职责链形式,战略形式…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的或许的圈复杂度,一起,还能够进步算法的保密性和安全性,运用哪个?自己衡量衡量吧。
看来你根底还不错,接下来再聊聊…
跋文
写这个文章呢,是因为我在实际工作中确实遇到了不少相似的场景,后期需求修正原需求的需求也见的不少,所以想着爽性就写下来吧。
今后我会持续把其他的规划形式也持续更下去
假如有帮到你,万分侥幸!