Java 中 dao 层和 service 层都使用接口,是不是为使用接口而使用接口?java
我的认为,若是没有搞懂为何用接口,那么有些人就会逢类就要实现接口……在一些业务不复杂的场景下,真的没有必要这样作,可是内心要明白。程序员
前面都说了不少例子和理论了,不过学习就是站在巨人的肩膀上,不断重复,概括和升华到方法论的过程,再重复一下,引用软件工程文献:spring
使用接口是为了把调用与实现解耦,带来的好处是能够各干各的,带来的坏处是从一个概念变成了两个概念,增长了系统的复杂度。数据库
衡量一下在具体场景中是弊大于利仍是利大于弊,就能够作选择了。编程
固然,在大部分场景下还要考虑一个因素,就是你会不会写接口。没有良好接口设计能力的人,写出来的接口抽象不合理,等于没写,什么好处都得不到,只有坏处,这种状况下干脆别写。安全
那怎么衡量你会不会写接口呢,个人经验是,至少见过一次写了接口后获得明确好处的例子。架构
最简单的场景,写接口的是 A,写实现的是 B。固然大多数相似状况不必真的建一个 interface,而后再让别人去 implement。mvc
另外一种状况,调用代码先于实现代码编写。好比 A 开发的是 struts2 这种框架,那 A 先得搞个 Action 接口,让程序先跑起来。oracle
dao 作数据库读写用的。对应上面那几种状况:框架
一、做为架构师想写两行代码就让小弟加班干活然则本身去泡妹子的话,可能须要写个 interface ,里面几个抽象的 insert、delete 之类的方法;
二、项目在快速原型阶段若是客户满意就掏钱买oracle,若是客户不满意就用免费MySQL的话,你可能须要定义个 dao 接口而后先用内存数据库写点能让原型跑起来的实现,等一切有定论了再说;
三、每一个类都有一个dao,每一个 dao 都有 crud 方法的话,你可能须要定义一个通用 Dao 接口,而后搞点代码,不用一个个的去写体力代码今后登上人生巅峰。因此dao接口仍是有用的。
service 做为业务逻辑的实现,得具体问题具体分析:
不去抠理论的话,什么是 service——就是一段实现了某个逻辑的代码组合。因此 service 是比 dao 更抽象的概念,严格来说 dao 就是一种 service。只不过在 java 开发中,dao 是我的人都得写的东西,因此都拿出来单说。所以,service 跟 dao 没有本质分别。
写上一层的时候,会用到下一层提供的逻辑,具体表现形式就是各类各样的 service 类和里面的方法。上一层开始的时候,必定会知道下一层会干什么事,好比 “将传入编号对应的人员信息设置为离职”,但下一层的代码不必定已经实现好。因此须要有个接口,让写上层代码的人先能把代码写下去。有各类理由能够支持这种工序的合理性,好比通常来讲,上一层的一行代码会对应下一层的好多行代码,那先让写上层代码的人写一遍,解决高端层面的bug,会提升不少效率。
不一样业务模块之间的共用,不必定是共用某段代码,也多是共用某段逻辑,这时候就须要抽象一个接口层出来,再经过不一样的注入逻辑实现。
好比模块1是登记学生信息,模块2是新闻发布,看上去风马牛不相及。但分析下来若是两个模块都有共同点,顺序都是;
一、验证是否有权限
二、验证输入参数是否合法
三、将输入参数转化为业务数据
四、数据库存取
那就能够写一个 service 接口,里面有上述 5 个方法,再分别写两个 service 实现。具体执行的时候,经过各类注入方法,直接 new 也好,用 spring 注入也好,实现不一样的效果。
java 的各类 mvc 框架都提供机制给你干这个事。每一个项目或产品,都应该能够用相似的思路抽象出一些东西,若是抽象合理,会很大程度的提升项目架构的合理性。
这些搞定,写个接口而后实现 mock 用于单元测试这种事,信手拈来。
说实话,总结到这里,都是以前的种种的疑问的解答和对概念理解的升华,也是为了复习用,可是说来讲去就是那些东西,高内聚,低耦合,开闭原则,单一职责原则,面向接口编程原则,业务和数据分离,工做内容分离,解耦,封装,多态,代码隐藏……其实就是反复这些东西的举例,早已经在十几年前就让前辈和大牛们玩烂的东西……
刚开始阅读《Thinging in Java》一书,有以下的说法:
接口与具体实现分离
这里的具体实现如何理解,这里仍是要联系到接口的重要使用目的之一:向上转型。
利用接口能够被多个类去实现的特征来分离工做内容,分离不一样的业务逻辑而去灵活的插拔不一样的但能够替换的实现方法。
例如,有不一样的动物,叫声不同,只须要定义一个”叫声(xxx)“方法,而让牛、羊、青蛙等等去具体实现这个“叫声(xxx)”方法,调用的时候只须要:
动物.叫声(xxx);
就能发出对应动物的叫声了,还要联系接口的一个重要使用目的之二:提供行为的统一的约束,避免实例化,也就是说和具体实现要分离。
在这里再简单重复一些内容:接口每每定义的是一些行为,在设计原则里面有一条“单一职责“的原则,接口的做用只是提供一些方法给你,它不关心你是怎么使用的。就像电脑的 USB 接口,咱们不须要关心这个 USB 接口是怎么实现的,咱们只需可以使用这个USB接口。
客户端不等于客户端程序员,可见性也不是针对程序员的,其实不少问题,站在计算机的角度去看就一目了然了。
客户端是指调用它的类或者具体对象,例如私有域不对具体的对象暴露,封装可以保证外部的对象或者实例不能修改它,从而保证了类的安全。
封装的做用不是真的把全部代码实现都让客户端程序员看不到,这个隐藏的目的是让客户端在调用方法等行为时可以按照编写 API 的程序员制定的规则来:一个变量不能让人家随便修改,也不能随便就能得到值,赋值要经过 setter,得到值需 getter 等方法,变量相应的就要经过 private 来隐藏。
举一个形象的例子,形容程序员给用户留了误操做的坑,用来描述因封装不当、没有对类作好隐藏而致使 API 使用问题也是能够的。
假设有一个自行车类,它有两个成员变量——轮子(wheel)和踏板(pedal),它但愿客户端能调用『踩踏板』这个方法前进,也能调用『换轮子』这个方法维修自行车,但决不能让用户在『踩踏板』的同时『换轮子』,甚至像图上那样把棍子插轮子的缝隙里,那么就须要把轮子变量设为 private——隐藏起来,不让用户自由获取,得按照你的规则来。
好比骑车的时候就只返回 null,中止的时候就能够返回正常——让用户能够作换轮子之类的工做。
setter 的做用也是相似的,都是为了保证客户端调用时可以遵循 API 设计者的规则,不然调用时就会出现各类不可控的乱象,轻则让调用者骂『这是哪一个 SB 设计的 API,代码写起来太难管理了』,重则会出现不少意想不到的 bug,也许实际上的封装应该是对轮子再作进一步的封装,那么就要用内部类的方式。
总之看书的时候对有些想好久也不能明白的东西不用太钻牛角尖,先照着书上说的作,等写了足够的代码,经验多一点了,天然就能理解一些东西为何要那样作了。