JAVA接口设置基本原则
Posted 沛沛老爹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA接口设置基本原则相关的知识,希望对你有一定的参考价值。
背景
昨天测试提醒有个功能报错了,我看了下,发现接口有过更新。
然后顺便问了下开发同学,确实有更新。
他们的更新是把现有的接口进行了修改。
导致返回的数据结构变更了,以至于接口直接报错。
其实负责后端开发的同学,开发时间也有4-5年了。
基于这个情况,我觉得,可以和大家分享下接口设计的几个点
设计原则
说明
类的设计原则不在本次讨论范围之类,如果对类设计的六大原则感兴趣的话,可以自行百度。
此处说明的接口设计,如无特别说明,泛指接口类和类中的方法设计。
常用的接口设计原则
1、如果使用了设计模式,在命名时需体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
正例:public Inteface OrderFactory;
2、接口类中的方法和属性尽量保持简洁性
接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。
尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。
例:接口方法签名 void commit();
接口基础常量 String FORMATE= "utf-8";
说明:JDK8 +后接口允许有默认实现,那么这个 default 方法,是对所有实现类都默认实现该默认方法。
例如下面的接口的默认方法,所有实现该接口的方法都将会继承serviceName方法
public interface UserService
public default void serviceName()
System.out.println("这是UserService ");
3、规范接口和实现类的命名
常用命名规则:
命名尽量清晰,不要为了省下几个字符而做一些没有意义的事情。
(1)对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部
的实现类用 Impl 的后缀与接口区别。
例:UserServiceImpl 实现 UserService 接口。
说明:一般这种接口都是基础的业务类接口比较居多。基本上业务代码都是这种格式命名的。
public interface UserService
//TODO
public Class UserServiceImpl implements UserService
//TODO
(2)如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形式)。
例:AbstractTranslator 实现 Translatable 接口。
说明:这种接口一般是公共类的接口。放在common里面的一般都是这种命名模式。
4、已经使用的接口,不要去修改方法签名
外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生
影响。
接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。
public class TestController
/**
* 测试接口
* 代替接口:test01
* @author admin
* @since 2020-06-03
*/
@ApiOperation(value = "测试")
@Deprecated(since = "1.2.0",forRemoval = true)
@RequestMapping(value = "/test", method = RequestMethod.GET)
public CommonResult<Map<String,String>> test()
return CommonResult.success();
@Deprecated注解(JDK1.9+)中可以标识过期版本,和是否在以后的版本中删除的标志
@Deprecated元素:since 和 forRemoval。
since: 元素指定已注解的API元素已被弃用的版本。
forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API。
5、泛型通配符使用需要注意对应的使用场景
泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。
PECS(Producer Extends Consumer Super)原则:
第一、频繁往外读取内容的,适合用<? extends T>。
第二、经常往里插入的,适合用<? super T>。
一般情况,大家适用的都是往外读取内容的情况比较多。
例如下面,这个是调用了第三方百度,往外读的情况
public interface IBaiduRequest<T extends BaseBaiduResponse>
//TODO
public abstract class BaseBaiduResponse implements Serializable
//TODO
6、接口入参保护
这种场景常见的是用作批量操作的接口。
就是如果一次性客户端来个大批量操作,到时候服务器就会出现类似DDOS的攻击情况了。例如像批量导入数据,一般的情况下都会是1000作为一个批次处理。因为Mybatis批量插入的sql还是有长度限制的。
7、对外提供的开放接口,需要进行参数校验。
社会用大量的写实电影来告诉你一个现实:除了自己,其他人都靠不住。
对外提供的开放接口,不管是 RPC/API/HTTP 接口,需要进行参数校验。
除了前端校验外,有的时候可能会绕过前端校验。
8、所有的接口方法必须要用 Javadoc 注释
所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
格式说明:对子类的实现要求,或者调用注意事项,请一并说明。这个不写,你可以问下前端同学的感受。
9、接口实现里的特殊注释标记,请注明标记人与标记时间
TODO等特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,
经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
待办事宜(TODO):( 标记人,标记时间,[预计处理时间])
表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc
还没有实现,但已经被广泛使用。只能应用于类,接口和方法。
public interface IBaiduRequest<T extends BaseBaiduResponse>
//TODO 张阿三 2022-04-25
10、对外的 http/api 开放接口必须使用“错误码”;而应用内部推荐异常抛出;
跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误简
短信息”。
说明:关于 RPC 方法返回方式使用 Result 方式的理由:
(1)不要以为对方就是用你以为的方式来编码。调用你接口的开发,能力和理解力都是一个考验,使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
(2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用
端来讲,他不一定看得懂。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输
的性能损耗也是个问题。
(3)如果你不小心把数据库操作的一些错误抛出去了,看得懂和看不懂都是一场灾难,具体原有你懂的。
11、不要写一个大而全的数据更新接口。
不要写一个大而全的数据更新接口。传入为 POJO 类,不管是不是自己的目标更新字
段,都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。
执行 SQL时,不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。
12、二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。
13、系统设计时,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护。
低层次模块依赖于高层次模块的抽象,方便系统间的解耦。
14、接口更新,只增加,不修改
你不清楚原来公布出去的接口,到底有多少设备在请求,调用端有没有做异常处理。如果没有的话,一旦你修改了返回的参数属性,导致原来调取的时候报错,就有可能导致APP闪退等情况存在,所以,对于接口的设计,宁愿返回多无用的字段,也不要删除原来的字段。
如果那个接口实在不用了,就另外启用一个新的接口,把原来的接口直接标为废弃。
例如,如果你把参数b直接修改为c。那么很有可能前端就悲剧了。
public class TestController
/**
* 测试接口
* 代替接口:test01
* @author admin
* @since 2020-06-03
*/
@ApiOperation(value = "测试")
@Deprecated(since = "1.2.0",forRemoval = true)
@RequestMapping(value = "/test", method = RequestMethod.GET)
public CommonResult<Map<String,String>> test()
Map<String,String> map = new HashMap<String,String>();
map.put("a","1");
map.put("b","2");
return CommonResult.success(map);
正常的写法一般是在后面增加返回参数c,让APP的新版本调用新参数c;
如果原来参数不要了,设置值为空。结果如下
public CommonResult<Map<String,String>> test()
Map<String,String> map = new HashMap<String,String>();
map.put("a","1");
map.put("b","");
map.put("c","2");
return CommonResult.success(map);
总结
基本上,接口的设计要求就上面几条了。
总结来说,就以下几点:
1、接口清晰,调用端看得懂,后面自己人修改也不麻烦;
2、外面的坏人很多,最好的办法是自己保护好自己,也就是说鲁棒性要杠杠的;
3、捡芝麻,也不要丢西瓜。修改接口不是重建,而是糊裱匠,顶级的糊裱匠,那是李合肥那种级别的,劝你就别想了,一般人也做不来。
4、...嗯,我还没想到,如果你想到了,就在评论里面加吧。
以上是关于JAVA接口设置基本原则的主要内容,如果未能解决你的问题,请参考以下文章