目录:
前言:
正文:
什么是API?
什么是SPI?
为什么会有SPI?
如何在我们的应用使用SPI?
前言:
(俗称牢骚,可忽略)
在去年的时候,一直认为研发团队(不是开发团队!)才能写出不改动原来的代码动态的扩展功能或者修改内容,传入不同的接口实现类组合实现热拔插的功能,这种看起来高大上的程序。
造成这种认知的原因很简单:
开发团队一直都是写业务的,而业务是不断地变化的,哪有什么共性可言?况且不加班就好啦,哪有时间想什么接口设计,人家研发团队就是提供组件的,研发就是抽出公共组件服务业务团队的!
不过随着接触一些书籍、朋友和同事,这种认知逐渐被打破,为何呢?看看下文吧!
正文:
什么是API?
API,全称:Application Programming Interface,意思就是应用程序编写接口,结合我们平时的开发翻译一下:来一个需求,首先定义一个接口,根据接口作出实现类,这样一个应用功能就是开发完毕,所以API的意思就是服务端编写好接口,并制定实现类,给第三方使用的一种规范。
以上就是一个简单的API接口的流程
什么是SPI?
SPI,全称:Service Provider Interface,意思就是服务提供程序接口。单纯给出这样一个翻译,可能很多开发的小伙伴看不太懂(如果没有了解过SPI的话),这里简单解释一下:它是定义好抽象类 / 接口来对外提供服务,具体使用哪个实现类,提供服务的服务者并不关心,他所提供的只是一种规范(接口就是定义规范的),举个例子:
public interface LogPlugin {
public void debug(String condition,Object... param);
public void info(String condition,Object... param);
public void warn(String condition,Object... param);
public void error(String condition,Object... param);
}
// Log4j实现
XX01 implements LogPlugin(){}
// Slf4j实现
XX02 implements LogPlugin(){}
public class UserServiceImpl implements UserService{
// 测试一个方法使用
modifierType returnType methodName(LogPlugin logPlugin,ParamterType params...){
logPlugin.info(condition,Console contents);
}
}
这里定义了一个插件LogPlugin,在我们应用中并不知情要使用哪个插件来打印我们的日志,具体的实现类是有调用者来给出,这样话,只有再想扩展或者使用其他的日志框架无需修改现有的程序,只需传入不同实现类进行扩展即可。
以上就是一个简单的SPI使用的流程。
为什么会有SPI?
在回答这个问题之前,我想看到这里的小伙伴心中应该有大致的想法了,它可以无需修改原来接口的情况下修改、扩展一些业务内容,所以才有SPI。
这里多唠叨一句吧,不知道小伙伴有没有了解过 设计模式–策略设计模式 其实这里的SPI与策略模式很是雷同(实现上),但并不完全相同,SPI更倾向一种设计理念,策略模式更是一种扩展实现,只是恰巧与SPI的实现有异曲同工之妙而已。如果大家没有了解过该设计模式,有兴趣的朋友可以自行学习一下,本文与至此就点到为止啦!
如何在我们的应用使用SPI?
SPI在上文也大致已经介绍,它是一种面向接口的设计思想,既然是面向接口的,而接口又是一种规范,所以我们第一步就是找出应用的变化部分和不变的部分;
这里以实际用的例子举例:
现在要开发一个结算系统,这个系统结算的类型有两种(未来可能是很多种),一种是按照订单结算,一种是按照活动结算,且结算的规则有实时结算,按天结算等未来也可能继续添加,需求就精简到这里吧。
第一步:找变化的部分和不变的部分。
这个怎么找?我怎么感觉都是变化的部分/都是不变的部分呢?
确实这个是难点,开发过几年,技术实现应该不是大问题,难的就是一眼识破其本质的能力,下面我简单说一条个人的规律吧:一般在产品提出需求的时候会有很多提示,根据产品的提示和自己的经验来寻找这两个部分;看一下上面的需求,结算的类型有两种,那么不变的就是是结算的类型(指的说的是类型,type)和结算的规则(指的是类型,rule),变化的是结算的类型(这里指的是具体实现,注意与前文语义不同,感叹一下汉语语言的精妙哈)和结算的规则(指的是实现类),就这样把变化和不变的部分找出来了,但是这个是只是一个小技巧,具体技巧之后有时间会另起一篇文章讲述,到时候会更新到这里。
第二步:根据不变的不同抽取接口,根据变化的部分抽取实现类
所以我们可以抽取结算类型接口:
public interface SettlementType{
public <T> T settlement(Message message);
}
public interface SettlementRule{
public <T> T getSettlementRule(Message message);
}
实现类这里就不写了,就这样,一个简单的SPI就出来了
当然更复杂的,这里时间有限,后续继续更新吧!