转自:http://blog.csdn.net/liujin163/article/details/3341725
现在大家都在提面向接口的编程,好处是显而易见的,程序扩展性强,接口定义了一个api规范,即定义了大家能够调用的api中各个java类对象的 java方法名称,大家都按照这个规范进行编程,如对java.sql包定义了jdbc的接口,所有编程者调用jdbc不需要考虑jdbc驱动是如何实现的。
不同厂商在提供同一类api的产品时,则需要实现这些接口,提供自己的实现类。这样api的使用者只对接口进行调用,而不管接口是如何实现的,如果他觉得厂商的接口实现不好,可以换一个厂商的实现类,甚至自己来实现,但确基本上不需要更改已经写好的应用程序(因为应用程序只对接口进行调用,而所有的实现类的接口都是相同的)。
但在开发应用时,如何对接口进行调用,如何实例化接口对象?你无法通过类似于new Inteface1()等来进行实例化,因为接口是不能实例化的(接口即Java的Interface不是类,所有方法都是抽象的,所以不能实例化,能够实例化的只能是接口的实现类)。实例化接口类对象主要有两种方法:
1) 直接new 接口的实现类的实例,类似于
Map map=new HashMap(); //HashMap实现了Map接口
虽然可以这样,但这与面向接口的编程的原则不符,因为你必须要知道接口的实现类,如果你有天感觉这个实现类不好,想换一个实现类,就麻烦了,你必须找到你以前的应用程序源代码,将每一个调用接口实现类的地方都改过来,工作量太大了。
2) 使用工厂,如
Interface1 instance=Factory.getInstance(); 或
Interface1 instance=Factory.create();
由工厂来提供实例化方法,你不必关心实例化类是哪一个。当然你必须知道工厂类是哪一个,更换接口实现的时候,你只需要改动工厂类的名字,其余的都不需要更改了。
举个jdbc调用的例子:
oracle的jdbc调用:
... ...
Class.forName("oracle.jdbc.driver.OracleDriver";);
Connection conn=DriverManager.getConnection(jdbcurl,username,password);
SQLServer的jdbc调用:
... ...
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
Connection conn=DriverManager.getConnection(jdbcurl,username,password);
MySQL的jdbc调用:
... ...
Class.forName("com.mysql.jdbc.Driver");
Connection conn=DriverManager.getConnection(jdbcurl,username,password);
Class.forName()是将一个类初始化,这里,调用不同厂商提供jdbc驱动时,使用了不同的jdbc驱动类的类名,该方法会初始化化该类,该类在初始化时new一个Driver实例(实际上是一个生产数据库连接Connection的工厂),并将Driver实例注册到 DriverManager中(的一个Vector中),再通过DriverManager中的getConnection()方法找到Driver实例,再调用Driver实例的getConnection()方法。
可以看出,只需要更改Driver(连接工厂)的类名,提供不同的jdbcUrl和用户、密码,就可以生成出一个数据库连接(Connection接口类的实例)。
建议:如果你使用配置文件来提供工厂类的名字,程序通过调用配置文件来读取工程的类名,更换接口实现就更简单了,你只需要改配置文件就可以了,而不需要更改程序。
接口和工厂之间关系形象比喻:
接口就好比是产品规格,如螺母和螺钉的产品规格,规定了不同的大小尺寸,左旋等,工厂好比生产这些螺母和螺钉的加工厂,而接口的实例好比不同工厂生成出的螺母、螺钉的产品。每一个不同的工厂虽然生成出的不同螺母和螺钉产品,但都必须遵守这个产品规格,这样不同工厂生产出来的不同的螺母和螺钉才可以拧在一起使用,你也可以随意更换不同工厂生产出来的螺母或螺钉,而不会出现问题。
当然还有第3种解决方案,就是spring容器,它会帮助你实现接口类的实例化,但spring容器也是通过工厂来生成实例的,只不过用的是容器内部的工厂。