在 Java 业务系统的开发中, service 层有必要写个接口吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
awesomeMen
V2EX    Java

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

  •  1
     
  •   awesomeMen 2021-03-12 10:15:29 +08:00 6652 次点击
    这是一个创建于 1675 天前的主题,其中的信息可能已经有所发展或是发生改变。
    感觉没啥卵用啊?有大佬出来解答一下吗?
    44 条回复    2022-04-13 09:48:53 +08:00
    RedBeanIce
        1
    RedBeanIce  
       2021-03-12 10:18:32 +08:00
    大部分系统没用。。。
    hello2060
        2
    hello2060  
       2021-03-12 10:21:32 +08:00
    接口那肯定是因为要复用啊,把公用的部分抽取出来,没这个需求就不用呗。
    justNoBody
        3
    justNoBody  
       2021-03-12 10:22:35 +08:00   2
    如果你这个类的工作简单,且业务上目前没有什么需要扩展的,没有什么变化点,我觉得是可以不写接口的。
    但是,如果这个 service 会存在多种实现的时候,比如你的 service 是需要调用运营商的短信接口,然后你们对接了多个运营商,这个时候接口就非常有必要了。
    有了这个接口以后,其他同学只需要根据你定义的接口,传入必要的参数,选择一个合理的实现(或者你用策略模式封装一下),就可以非常简单的发出短信。
    个人见解,欢迎讨论
    awesomeMen
        4
    awesomeMen  
    OP
       2021-03-12 10:22:59 +08:00
    @hello2060 为什么复用就要加个接口?直接在 service 层写方法不行吗?
    yeqizhang
        5
    yeqizhang  
       2021-03-12 10:24:21 +08:00
    面向接口编程... 主要是接口代理、接口继承、极小的可能直接改成 rpc...
    wakzz
        6
    wakzz  
       2021-03-12 10:24:59 +08:00
    绝大多数系统用不上接口抽象,后来就很少用接口了,除非一开始就确定这快逻辑有多个实现类所以要抽象个接口出来。
    hafuhafu
        7
    hafuhafu  
       2021-03-12 10:27:48 +08:00
    你要是保证项目所有 service 从现在到将来都只有一个实现,那肯定没用啊。
    这个就是个规范而已,约定优于配置。你喜欢的话不要 service 都行。而且业务系统这种基本类和接口都是自动生成的,在意这个干吗...不就接口加一个方法,实现类实现一下的事。
    KarmaWu
        8
    KarmaWu  
       2021-03-12 10:28:01 +08:00
    看情况,假设要区分多个版本的接口,须有不同的实现类
    securityCoding
        9
    securityCoding  
       2021-03-12 10:29:01 +08:00
    1. 如果都是内部自己调用,那没有必要,一个 class 撸到底就行
    2. 如果有外部 rpc 调用的话,还是定义一个接口方便打包到 client-sdk 给别人
    yeqizhang
        10
    yeqizhang  
       2021-03-12 10:34:40 +08:00
    @yeqizhang 才发现说错了,接口哪有继承....修正:接口多个实现
    chendy
        11
    chendy  
       2021-03-12 10:35:07 +08:00
    大部分业务场景用不上,能想到的需要用到的场合:
    1. 上古系统没有 cglib 之类的只能用 Proxy 所以必须接口
    2. 需要多个实现
    3. 要分发 service 层代码
    soupu626
        13
    soupu626  
       2021-03-12 10:38:03 +08:00
    基本原理就是,大家先约定好,要什么参数,什么返回,然后就可以不关心底层细节和上层逻辑,各干各的就行了,还有就是结对的时候,便于测试,定好接口,一个写实现,一个写测试,本质上是为了解耦
    但是现在大家都是一个人一把梭,就是个象征意义,可以不加
    Macolor21
        14
    Macolor21  
       2021-03-12 10:43:21 +08:00 via iPhone
    提问之前善用搜索引擎,了解下提问的艺术,这个问题在 StackOverFlow 已经有人提问,并且多个答案很好的解释了。/questions/55087578/do-i-really-need-to-create-interfaces-in-spring

    总结来说,这样的写法是用于扩展用(利用 DI ),你常规理解的用法,这个 service 有不同实现,然后就是 DI 的好处了。

    但如果你的 service 不需要另外的实现,你可以不用写,Spring IOC 也能将你的 service 注入到其他类中。现在微服务盛行,没啥机会让你为一个 service 写多个实现了。

    但是基于接口,你可以在测试时,不使用生产的业务,通过写一个 Mock 实现,配置不同的配置文件,来测试你的这个 service 接口。
    zjsxwc
        15
    zjsxwc  
       2021-03-12 10:45:47 +08:00   9
    用 interface,是为了能够方便地在屎山上拉屎啊,

    不用 interface,你需要挖开老屎,看老屎里面有啥,用来 interface,你只管在老屎上面拉新屎。
    akiakiseofficial
        16
    akiakiseofficial  
       2021-03-12 10:53:19 +08:00
    先说结论,有必要。

    就算只有一个实现类,也应该保留接口。这种情况下,通过查看接口,我能很快的找到对外暴露的方法,后续维护重构都很省心。

    如果不保留接口,那我在查看类的功能时候就得遍历所有的 public 方法,时间一长,转手的人一多,这个类就会无限膨胀下去,最终变成屎山。
    zjsxwc
        17
    zjsxwc  
       2021-03-12 10:58:00 +08:00
    interface 无非就是给屎打上标记而以,这是黑色的屎、这是黄色的屎,这是硬的屎,这是稀的屎,
    这个坑只能拉 又黄有稀的屎,我就拉 又黄有稀的屎,我 tm 才不管你这个坑里面有什么屎,以前有谁在这个坑里面拉屎。



    如果屎没有各种标记分类,就相当于,这是张三拉的屎,这是赵四拉的屎,tm 的这个坑居然只能张三来拉屎,我要拉屎,只能让张三来代替我拉屎了,张三又不在,怎么办啊,只能把我变成张三,很累的好不。
    zjsxwc
        18
    zjsxwc  
       2021-03-12 11:01:38 +08:00
    当然,我可以继承张三啊,我就又可以拉屎了,可是很多时候,我虽然继承了张三,但我看不惯张三,张三吃素,我吃肉的,根本就是格格不入
    sleeepyy
        19
    sleeepyy  
       2021-03-12 11:01:48 +08:00
    @zjsxwc 直接看呕吐了。。。
    zjsxwc
        20
    zjsxwc  
       2021-03-12 11:13:18 +08:00
    当然,你可以一个屎填坑,这个坑永远只能拉这个屎,有需求,就改造这个屎本身,也就是楼主主题里说的。

    我明明能一个屎完事,这个 final 屎就是牛逼!没有毛病。
    zm8m93Q1e5otOC69
        21
    zm8m93Q1e5otOC69  
       2021-03-12 11:24:51 +08:00
    接口顶层,比如说 IDeviceService 然后可以有多个实现类 DefaultDeviceServiceImpl 、xxxDeviceServiceImpl ... 引用的时候用 @Resauce 指定具体哪个实现类?我觉得这样比较好。但是实际工作中,都是一个接口实现类撸到底
    zjsxwc
        22
    zjsxwc  
       2021-03-12 11:28:37 +08:00
    先贤们是拒绝改造这个屎本身的,
    改造本身意味着你之前拉的这个屎,“有 bug”,只有不完美的屎才需要被再次改造,
    这是对你拉屎技术的侮辱,

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

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

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

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

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

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

    外部集成类型因为会有多种实现的可能性,考虑到扩展实现所以会使用接口来做。
    superliuliuliu1
        39
    superliuliuliu1  
       2021-03-12 16:24:28 +08:00
    @zjsxwc 味道十足
    hehe12980
        40
    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 。 后面再加更多的类型 这一块也是不需要动的。
    例子有很多很多,抽象很重要,觉得不重要的,武断的说一句,大概率是个菜鸡。
    sha851092391
        41
    sha851092391  
       2021-03-12 17:42:44 +08:00
    @sha851092391 还有一种情况业务方法也要使用接口,就是你的 service 写太多乱七八糟的方法了,这时候可以通过接口去规范提供对外的调用方法。
    54kael
        42
    54kael  
       2021-03-13 16:05:32 +08:00 via Android
    @zjsxwc 求你换个比喻吧
    linbiaye
        43
    linbiaye  
       2021-03-22 13:12:41 +08:00
    在 spring 的和 cglib 的加持下,没有必要。如果一个 interface 只有一个实现,这种抽象的意义在哪里?
    Nois9527
        44
    Nois9527  
       2022-04-13 09:48:53 +0800
    @zjsxwc 太形象太对了,我终于知道该怎么对别人描述这个事了!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5435 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 01:22 PVG 09:22 LAX 18:22 JFK 21:22
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86