在 Java 业务系统的开发中, service 层有必要写个接口吗?

2021-03-12 10:15:29 +08:00
 awesomeMen
感觉没啥卵用啊?有大佬出来解答一下吗?
6142 次点击
所在节点    Java
44 条回复
zm8m93Q1e5otOC69
2021-03-12 11:24:51 +08:00
接口顶层,比如说 IDeviceService 然后可以有多个实现类 DefaultDeviceServiceImpl 、xxxDeviceServiceImpl ... 引用的时候用 @Resauce 指定具体哪个实现类?我觉得这样比较好。但是实际工作中,都是一个接口实现类撸到底
zjsxwc
2021-03-12 11:28:37 +08:00
先贤们是拒绝改造这个屎本身的,
改造本身意味着你之前拉的这个屎,“有 bug”,只有不完美的屎才需要被再次改造,
这是对你拉屎技术的侮辱,

所以讲究的先贤们提倡用 interface,避免改造屎本身,除非真有 bug 才去改造屎本身,别动老屎!

当然不讲究的我,改个屎而以,给钱就行!
clf
2021-03-12 11:46:50 +08:00
JDK8 接口可以使用 default 关键字在接口里提供默认的抽象方法的实现:
依据这几个特性,我可以封装一些泛型接口,提供可复用的泛型方法,实现类里只需要写业务部分的代码,由于大部分的服务都是继承于相同逻辑的接口,这样无论是基本的异常处理、日志记录、逻辑删除控制等等都能统一,并且在未来对这种统一的逻辑进行调整的时候,也只需要修改一个地方。如果某种业务有其它的逻辑,可以重写接口的默认实现。

接口可以多重继承(一个类实现多个接口):
在微服务场景中,一个服务可能被其它微服务调用,也可能被自己这个微服务里的其它 Service 类或者 Controller 调用,两边能调用的方法有相同的,也有不同的,通过提供不同的接口来控制方法的可见性。
HelloWorld556
2021-03-12 11:54:00 +08:00
这是一篇有味道的文章
xuanbg
2021-03-12 11:55:20 +08:00
没有逻辑上的必要性,但有工程上的必要性。一来我可以只写接口,别人去写实现。二来方便复制粘贴生成其他代码的基本结构及注释。
miv
2021-03-12 12:27:02 +08:00
@zjsxwc 哈哈哈哈哈哈哈笑死我了
ychost
2021-03-12 12:36:26 +08:00
个人觉得最佳实践,当需要提供 RPC 调度的时候可以加接口(毕竟 RPC 要提供 二方包),当内部相互调用的 Service 就没必要写,啰里啰嗦,改起来也麻烦,除非有那种根据配置切换 Service 的需求,但是这种一般通过 SPI 来做
lff0305
2021-03-12 12:41:05 +08:00
写个接口再写实现是基于以下的原因:
1. "面向接口编程"的实践
2. spring aop, 很久以前基于 jdk 动态代理的实现必须要接口,但是后来 cglib 稳定之后就可以不需要。所以接口-实现这个习惯就保留下来了。
liuzhaowei55
2021-03-12 12:58:37 +08:00
给自己业务用就没必要,对外开放就有必要。
CrazyBoyFeng
2021-03-12 13:48:10 +08:00
我给三楼的举例补充一下代码说明:
```
运营商 Interface 运营商=null;
switch(用户.get 运营商()):
case 移动:
运营商=new 移动();
break;
case 联通:
运营商=new 联通();
break;
try{
运营商.发短信();
}catch(短信失败 Exception e){
运营商.打电话();
}
```

这段代码不用接口的话,实现相同功能可能需要写出许多重复代码。
cmsyh29
2021-03-12 13:50:24 +08:00
额。我以为这是以前只支持 JDK PROXY 的遗留...
vate32
2021-03-12 14:21:55 +08:00
我觉得,如果只是一个定义只有一个实现这种方式的话,没有必要搞了,费事也令人费解。但是倒是可以使用这种接口+实现的结构,实现一些设计模式,比如说这两天就在用的责任链模式。。。
kifile
2021-03-12 14:47:04 +08:00
多人开发的时候是有意义的,找一个人把接口定义写了,后续别的同学基于接口定义,各自写各自的逻辑,挺好的。

各种测试逻辑也可以同步开始
oneisall8955
2021-03-12 15:30:54 +08:00
emmm,上个月才看到类似帖子 /t/747463
robinWu
2021-03-12 15:33:11 +08:00
你需要 mock 单测吗?
litchinn
2021-03-12 15:59:50 +08:00
这是个历史遗留,完全可以不用
litchinn
2021-03-12 16:09:06 +08:00
@litchinn 当然如果使用 SPI 之类的机制则另说
sha851092391
2021-03-12 16:20:02 +08:00
首先看你的 service 是什么类型,是业务逻辑类型还是外部集成类型(例如调用 xx 短信渠道发短信)。

业务逻辑类型其实是基本上不需接口,如果非得考虑什么接口多半是因为考虑到以后 service 变成 rpc 调用,但是实际上就算变成 rpc 调用,业务逻辑类型的上层也会再包装一层,所以基本上不会用接口。

外部集成类型因为会有多种实现的可能性,考虑到扩展实现所以会使用接口来做。
superliuliuliu1
2021-03-12 16:24:28 +08:00
@zjsxwc 味道十足
hehe12980
2021-03-12 17:40:24 +08:00
我个人认为 service 这层一定是要写接口的,最主要是为了抽象,当然有很多开发的人根本不明白写接口背后的意义,大部分都是要求写就写,先说结论写接口是为了抽象(可能你觉得是废话),随便假定一个场景把,比如我有一个 IA service 接口这个接口,处理一个很纯粹的业务逻辑。
场景 1:比如说步骤 1.是操作数据(必须要调 IBService,ICservice) 步骤 2.是保存数据,一开始小张这么写,它一个个调 IBServcie.operate,ICService.operate,保存也是如此,有一天业务变更,突然 IDService 也要存,继续改代码,这个时候就没有想过把公共方法抽出来比如叫 ISaveService,IOperateService,注入到 list 里,遍历去操作,去存。以后再加其他的 IEservice 这一块的代码不用动了。只需要写 IEservice 的逻辑
场景 2:再比如说步骤 1,是操作数据(根据类型,可能调 IBService,也可能要调 ICService,但是只调其中一个就能把业务完成了), 步骤 2 是保存数据,小张写了个 if,else 去干 后面随便类型的增多 if,else 越来越长就跟屎山一样,如果一开始就把类型做成特征比如说 IOperateService 定义两个方法 supportType ( type )以及 operate(params) 然后 IBService,ICService 去实现这个 IOperateService 接口,注入这个 List<IOperateService> list 遍历去找适配的类型的 Service, 去 operate 。 后面再加更多的类型 这一块也是不需要动的。
例子有很多很多,抽象很重要,觉得不重要的,武断的说一句,大概率是个菜鸡。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/760924

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX