1、背景
本人做抵押贷金融系统开发,最近在开发过程中,遇到一个新的需求,公司和原第三方支付公司有一定的矛盾,造成支付能力支持不足,公司内部进行的新支付系统的搭建,所以需要将原支付功能对接到新支付功能上去,并且是实现热切换,避免上线后,新支付系统或本系统出现问题,热切换到旧支付系统上面去,保证业务的实时性。
首先,统计了所以需要支付能力的接口,包括,用户发送四要素确认短信验证码,绑卡确认,用户换卡,系统批量代扣,催债代扣,微信支付宝主动还款,催债主动还款等功能,改造的影响范围大,前面4个接口影响用户进件以及使用,后面4个接口影响用户的还款,资产的回收都可能造成公司资产损失,并且开发周期短,旧支付随时可能停止支持,所以在这种情况下,第一步就想到对情况进行总结,然后进行设计模式设计,提高开发效率。
2、分析
首先,可以在线上热切换,不外乎读热配置中心,通过config 推送配置,读redis 不失效key 每次启动加载到redis,每次读数据库,因为公司没有使用热配置中心,线上去修改redis key 比较麻烦,然后采用了读数据库的字典配置,然后通过查表获取数据选择支付能力系统。其次,根据分析出的接口,可以得出,新旧支付能力都需要实现背景出现的功能接口点,根据模板方法的定义:模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。在选择不同的新旧系统能力时,即意味着不同的来使用不同的子类实现相同的功能,并且不在意新旧系统是如果具体实现的。
3、类图
PayChannel 接口定义支付能力的接口能力,然后有两个新的支付能力实现类,实现具体的功能,通过PayChannelContext 获取具体实现的子类,通过context 去调用,通过构造方法,每次去获取数据库里面的字典值,然后切换具体的实现类。有一个点,context 不能交给spring 管理,应该spring是单例的,并且每次生成后,context具体实现固定,payChannel 属性指定的子类地址不能被修改,所以context 不被spring 管理 ,在非spring 管理类中,实例化spring 管理类,需要重写ApplicationAwareContext的setApplicationContext 方法,并且通过getBean方法获取类实例
public class PayChannelContext {private static final Logger logger = LoggerFactory.getLogger(PayChannelContext.class);private static final String PAY_CAHNNEL_KEY_NAME = "PAY_CHANNEL_KEY";private static final String PAY_CHANNEL_NEW = "PAY_CHANNEL_NEW";private static final String PAY_CHANNEL_OLD = "PAY_CHANNEL_OLD";private PayChannel payChannel;public PayChannelContext() throws Exception {A a= new A();a.setKeyName(PAY_CAHNNEL_KEY_NAME);AFegin aFegin = BeanContextAware.getBean(AFegin.class);List<A> aList = aFegin.findlist(parmDic);if (aList .isEmpty()) {throw new Exception();}a = aList.get(0);String payChannel = a.getOptCode();if (PAY_CHANNEL_NEW.equals(payChannel)) {this.payChannel = BeanContextAware.getBean(NewPayChannel.class);} else if (PAY_CHANNEL_OLD.equals(payChannel)) {this.payChannel = BeanContextAware.getBean(OldPayChannel.class);} else {throw new Exception("");}}
}
@Component
public class BeanContextAware implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}public static <T> T getBean(Class<T> beanClass) {return context.getBean(beanClass);}
}
4、优势
本次开发过程可以形成一个公用的经验,金融系统调用第三方系统的业务点多,影响范围大,第三方系统类型多。通过此次开发,可以对第三方系统接口进行改造,提高系统的兼容性。并且这种模式扩展能力强,如果需要对接第三个、第四个三方系统,只需要实现相同的接口,然后通过修改context的构造方法即可,避免了对原业务代码的入侵,减少开发过程可能需要的错误
5、总结
开发过程中,要多思考代码的扩展性,维护性,重复利用性,等程序本身的健壮上来。我比较讨厌那种一来公司,就是个大头兵,就说公司代码该上什么docker ,k8s,用什么redis 分布式锁的人,我认为新技术,新工具是提高系统整体性的东西,代码的质量还要要从底层做起,自己做代码开发,首先着眼于代码本身的东西,如果以后未来本身的能力有所提高,再考虑其他的东西对系统的提升