zoukankan      html  css  js  c++  java
  • 接口的一些疑问

    1、Java 开发的 dao 层和 service 层都使用接口,是否是为使用接口而使用接口?

    Java 中 dao 层和 service 层都使用接口,是否是为使用接口而使用接口?

    个人认为,如果没有搞懂为什么用接口,那么有些人就会逢类就要实现接口……在一些业务不复杂的场景下,真的没有必要这样做,但是心里要明白。

    前面都说了很多例子和理论了,不过学习就是站在巨人的肩膀上,不断重复,归纳和升华到方法论的过程,再重复一下,引用软件工程文献:

    使用接口是为了把调用与实现解耦,带来的好处是可以各干各的,带来的坏处是从一个概念变成了两个概念,增加了系统的复杂度。

    衡量一下在具体场景中是弊大于利还是利大于弊,就可以做选择了。

    当然,在大部分场景下还要考虑一个因素,就是你会不会写接口。没有良好接口设计能力的人,写出来的接口抽象不合理,等于没写,什么好处都得不到,只有坏处,这种情况下干脆别写。

    那怎么衡量你会不会写接口呢,我的经验是,至少见过一次写了接口后得到明确好处的例子。

    1.1、什么情况下需要各干各的?

    最简单的场景,写接口的是 A,写实现的是 B。当然大多数类似情况没必要真的建一个 interface,然后再让别人去 implement。

    另一种情况,调用代码先于实现代码编写。比如 A 开发的是 struts2 这种框架,那 A 先得搞个 Action 接口,让程序先跑起来。

    1.2、回答问题

    dao 做数据库读写用的。对应上面那几种情况:

    1、作为架构师想写两行代码就让小弟加班干活然则自己去泡妹子的话,可能需要写个 interface ,里面几个抽象的 insert、delete 之类的方法;

    2、项目在快速原型阶段如果客户满意就掏钱买oracle,如果客户不满意就用免费MySQL的话,你可能需要定义个 dao 接口然后先用内存数据库写点能让原型跑起来的实现,等一切有定论了再说;

    3、每个类都有一个dao,每个 dao 都有 crud 方法的话,你可能需要定义一个通用 Dao 接口,然后搞点代码,不用一个个的去写体力代码从此登上人生巅峰。所以dao接口还是有用的。

    service 作为业务逻辑的实现,得具体问题具体分析:

    不去抠理论的话,什么是 service——就是一段实现了某个逻辑的代码组合。所以 service 是比 dao 更抽象的概念,严格来讲 dao 就是一种 service。只不过在 java 开发中,dao 是个人人都得写的东西,所以都拿出来单说。因此,service 跟 dao 没有本质分别。

    1.2.1、从工序上说

    写上一层的时候,会用到下一层提供的逻辑,具体表现形式就是各种各样的 service 类和里面的方法。上一层开始的时候,一定会知道下一层会干什么事,比如 “将传入编号对应的人员信息设置为离职”,但下一层的代码不一定已经实现好。所以需要有个接口,让写上层代码的人先能把代码写下去。有各种理由可以支持这种工序的合理性,比如一般来说,上一层的一行代码会对应下一层的好多行代码,那先让写上层代码的人写一遍,解决高端层面的bug,会提高很多效率。

    1.2.2、从抽象角度说

    不同业务模块之间的共用,不一定是共用某段代码,也可能是共用某段逻辑,这时候就需要抽象一个接口层出来,再通过不同的注入逻辑实现。

    比如模块1是登记学生信息,模块2是新闻发布,看上去风马牛不相及。但分析下来如果两个模块都有共同点,顺序都是;

    1、验证是否有权限

    2、验证输入参数是否合法

    3、将输入参数转化为业务数据

    4、数据库存取

    那就可以写一个 service 接口,里面有上述 5 个方法,再分别写两个 service 实现。具体执行的时候,通过各种注入方法,直接 new 也好,用 spring 注入也好,实现不同的效果。

    java 的各种 mvc 框架都提供机制给你干这个事。每个项目或产品,都应该可以用类似的思路抽象出一些东西,如果抽象合理,会很大程度的提高项目架构的合理性。

    这些搞定,写个接口然后实现 mock 用于单元测试这种事,信手拈来。

    说实话,总结到这里,都是之前的种种的疑问的解答和对概念理解的升华,也是为了复习用,但是说来说去就是那些东西,高内聚,低耦合,开闭原则,单一职责原则,面向接口编程原则,业务和数据分离,工作内容分离,解耦,封装,多态,代码隐藏……其实就是反复这些东西的举例,早已经在十几年前就让前辈和大牛们玩烂的东西……

    2、“接口与具体实现分离”,具体实现到底是指什么?

    刚开始阅读《Thinging in Java》一书,有如下的说法:

    接口与具体实现分离

    这里的具体实现如何理解,这里还是要联系到接口的重要使用目的之一:向上转型。

    利用接口可以被多个类去实现的特征来分离工作内容,分离不同的业务逻辑而去灵活的插拔不同的但可以替换的实现方法。

    例如,有不同的动物,叫声不一样,只需要定义一个”叫声(xxx)“方法,而让牛、羊、青蛙等等去具体实现这个“叫声(xxx)”方法,调用的时候只需要:

    动物.叫声(xxx);

    就能发出对应动物的叫声了,还要联系接口的一个重要使用目的之二:提供行为的统一的约束,避免实例化,也就是说和具体实现要分离。

    在这里再简单重复一些内容:接口往往定义的是一些行为,在设计原则里面有一条“单一职责“的原则,接口的作用只是提供一些方法给你,它不关心你是怎么使用的。就像电脑的 USB 接口,我们不需要关心这个 USB 接口是怎么实现的,我们只需能够使用这个USB接口。

    3、封装之后的类,源代码还是能被使用的程序员看到,怎么就说接口起到隐藏的作用?

    客户端不等于客户端程序员,可见性也不是针对程序员的,其实很多问题,站在计算机的角度去看就一目了然了。

    客户端是指调用它的类或者具体对象,例如私有域不对具体的对象暴露,封装能够保证外部的对象或者实例不能修改它,从而保证了类的安全。

    封装的作用不是真的把所有代码实现都让客户端程序员看不到,这个隐藏的目的是让客户端在调用方法等行为时能够按照编写 API 的程序员制定的规则来:一个变量不能让人家随便修改,也不能随便就能获得值,赋值要通过 setter,获得值需 getter 等方法,变量相应的就要通过 private 来隐藏。

    举一个形象的例子,形容程序员给用户留了误操作的坑,用来描述因封装不当、没有对类做好隐藏而导致 API 使用问题也是可以的。

    假设有一个自行车类,它有两个成员变量——轮子(wheel)和踏板(pedal),它希望客户端能调用『踩踏板』这个方法前进,也能调用『换轮子』这个方法维修自行车,但决不能让用户在『踩踏板』的同时『换轮子』,甚至像图上那样把棍子插轮子的缝隙里,那么就需要把轮子变量设为 private——隐藏起来,不让用户自由获取,得按照你的规则来。

    比如骑车的时候就只返回 null,停止的时候就可以返回正常——让用户可以做换轮子之类的工作。

    setter 的作用也是类似的,都是为了保证客户端调用时能够遵循 API 设计者的规则,否则调用时就会出现各种不可控的乱象,轻则让调用者骂『这是哪个 SB 设计的 API,代码写起来太难管理了』,重则会出现很多意想不到的 bug,也许实际上的封装应该是对轮子再做进一步的封装,那么就要用内部类的方式。

    总之看书的时候对有些想很久也不能明白的东西不用太钻牛角尖,先照着书上说的做,等写了足够的代码,经验多一点了,自然就能理解一些东西为什么要那样做了。 

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    明年,我们依然年轻
    总有些东西会如台风一样的来
    ora00257错误处理方法
    ORACLE登录错误的解决方法
    C#中public new void add()的new在这里的意义
    Oracle Form Builder配置问题的一些总结
    作为程序员,你应该知道的职场晋升之路
    ORA01034错误的解决方法
    【转】JQUERY刷新页面
    【转】对C# 中堆栈,堆,值类型,引用类型的理解
  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/10340267.html
Copyright © 2011-2022 走看看