dljd_001_通过接口降低代码的耦合度(1)
在学习jdbc之前我们首先来简单学习一下什么是面向接口编程?面向接口编程是怎么降低程序之间的耦合度的、使用面向接口编程给我们带来了那些好处?
一、需求
比如说有这样一个需求、让我们用java程序来简单模拟一个人驾驶(启动和停止)一辆宝马车的。当大部分人看到这个需求之后就会觉得这是不是也太简单了、当然这大部分人设计出来的代码基本是如下的情况:
1.1绝大部分人会有如下的实现:
1.1.1设计人类
package edu.aeon.driver; /** * [说明]:人类 * @author aeon */ public class Person { /** * 当一个类中以属性的方式引入其它类时、那么这种方式被称为聚合 */ private BMW bmw; public BMW getBmw() { return bmw; } public void setBmw(BMW bmw) { this.bmw = bmw; } /** * 驾驶 */ public void driver(){ bmw.run(); bmw.stop(); } }
1.1.2设计宝马类
package edu.aeon.driver; /** * [说明]:宝马类 * @author aeon * */ public class BMW { /** * 宝马的启动 */ public void run(){ System.out.println("启动宝马、行驶..."); } /** * 宝马的停止 */ public void stop(){ System.out.println("停止宝马!"); } }
1.1.3测试类:
package edu.aeon.driver; /** * [说明]:人驾驶宝马测试类 * @author aeon */ public class TestDriver { public static void main(String[] args) { Person person=new Person(); BMW bmw=new BMW(); person.setBmw(bmw); person.driver(); } }
1.1.4测试结果:
我们可以看出来、这个需求是实现了、但是呢这样设计出来的程序是否合理呢?用合成和聚合(关于合成/聚合的含义详见本文档总结部分)关系实现人驾驶宝马是否合理呢?答案是no!
2.1也有大部分会这么实现
2.1.1设计人类
package edu.aeon.driver; /** * [说明]:人类 * @author aeon */ public class Person { /** * 驾驶 * @param bmw */ public void driver(BMW bmw){ bmw.run(); bmw.stop(); } }
2.1.2设计宝马类
package edu.aeon.driver; /** * [说明]:宝马类 * @author aeon * */ public class BMW { /** * 宝马的启动 */ public void run(){ System.out.println("启动宝马、行驶..."); } /** * 宝马的停止 */ public void stop(){ System.out.println("停止宝马!"); } }
2.1.3测试类
package edu.aeon.driver; /** * [说明]:人驾驶宝马测试类 * @author aeon */ public class TestDriver { public static void main(String[] args) { Person person=new Person(); BMW bmw=new BMW(); person.driver(bmw); } }
2.1.4测试结果
显然我们看出第二种设计方法比较合理、但是这样设计出来的程序人类和宝马类严重的耦合在一起。当需求发生变化时(如:驾驶一辆奔驰/法拉利/大牛/p1)、我们又要去修改源码。那么怎么设计出来程序比较完美呢?请看第三种设计(面向接口设计/编程)。
3.1极少部分人设计(非常有经验的人会这么设计)
3.1.1设计人类
package edu.aeon.driver; /** * [说明]:人类 * @author aeon */ public class Person { /** * 驾驶 * @param car 注意此处:面向接口 */ public void driver(Car car){ //注意 car.run(); car.stop(); } }
3.1.2设计一个汽车接口
package edu.aeon.driver; /** * 汽车接口 * @author aeon */ public interface Car { void run(); void stop(); }
3.1.3设计宝马类
package edu.aeon.driver; /** * [说明]:宝马类 * @author aeon * */ public class BMW implements Car{ /** * 宝马的启动 */ public void run(){ System.out.println("启动宝马、行驶..."); } /** * 宝马的停止 */ public void stop(){ System.out.println("停止宝马!"); } }
3.1.4测试类
package edu.aeon.driver; /** * [说明]:人驾驶宝马测试类 * @author aeon */ public class TestDriver { public static void main(String[] args) { Person person=new Person(); Car car=new BMW(); person.driver(car); } }
3.1.5测试结果截图:
那么这种情况可以说设计出来的程序完全符合java力荐的面向接口编程、此处也实现了多态、动态绑定、当需求发生变化时(比如我们现在要驾驶一辆法拉利)、只需要写个法拉利类实现Car这个接口并且重写其实现方法即可。如:
package edu.aeon.driver; /** * [说明]:法拉利类 * @author aeon * */ public class Ferrari implements Car { /** * 法拉利的启动 */ @Override public void run() { System.out.println("启动法拉利、行驶..."); } /** * 法拉利的停止 */ @Override public void stop() { System.out.println("停止法拉利!"); } }
Person类不用我们做任何的修改(解耦)。可见面向接口编程给我们带来的巨大便利。
四、关于合成和聚合:
1.合成和聚合关系有整体和局部的意义。比如人是一个整体、而这个人的心脏/胃/...是局部、这里的心脏/胃/...的人体器官是人的一部分。图例如下:
人和心就是整体和局部的关系。人和车是吗?不是吧!
五、总结
1.合成/聚合有整体和局部的关系。
2.一个类成为另一个类的方法参数或者返回值、那么这种关系叫做依赖关系。这里显然人依赖于车才能驾驶、如果没有车那驾驶什么?但是程序中一定要依赖接口去实现。不然还是耦合在一起。
3.以后我们设计到定义变量类型、方法的参数类型及返回值类型一律使用顶层接口方式。