gembin

OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

HBase, Hadoop, ZooKeeper, Cassandra

Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

About Me

 

接口和工厂

1 基本信息

摘要:本文说明在Java API设计中,面向接口编程的思想,以及接口和工厂的关系。

作者:陈光耀

2 正文

  现在的java API的设计中,提倡面向接口的编程,即在API的设计中,参数的传递和返回建议使用接口,而不是具体的实现类,如一个方法的输入参数类型应该使用Map 接口,而不是HashMap或Hashtable等具体的实现类。这样做的好处是,程序容易扩展。如果使用Map作为参数,用户可以使用任何实现Map接 口的类作为参数,而不是仅仅限制使用HashMap或Hashtable作为参数,使程序的实现更加灵活。

  接口(Java的Interface),只定义了一些抽象的方法(也可以定义一些常量,但不鼓励这么 做),没有具体的实现,这些抽象方法的具体实现,必须由实现(implements)接口的具体类来实现。接口反映了系统设计人员对系统的抽象理解。实际 上,接口只是定义了一个规范和约束,要求实现类必须遵循这些规范和约束,大家编程时都要按照这个规范进行编程。

  使用接口进行编程时,API的使用者只使用接口进行调用,而不管接口是如何实现的,这样你就可以随意更 换接口的实现厂商(provider)。假如,你觉得厂商的接口实现不好,可以换一个厂商的实现类,甚至自己来实现,但确基本上不需要更改已经写好的应用 程序(因为应用程序只对接口进行调用,而所有的实现类的接口都是相同的)。

  如java.sql包定义了很多jdbc的接口,大家在进行jdbc编程时都只需要使用 Connection、PreparedStatment、ResultSet等接口,而不需要考虑这些接口的具体实现类是什么。每种数据库,如 Oracle、SQLServer、DB2的JDBC驱动则是由各个厂商提供具体实现类,来实现统一的JDBC API接口规范。

  面向接口的编程中,工厂(Factory)的使用恐怕是最常用的,接口和工厂的关系是密不可分的,工厂,简单的说,就是生产接口实现类实例的类。可以说,在面向接口的编程中,工厂方法发挥了极其重要的作用。

  在开发应用时,如何对接口进行调用,如何实例化接口对象?你无法通过类似于new Inteface1()等来进行实例化,因为接口是不能实例化的(接口即Java的Interface不是类,所有方法都是抽象的,所以不能实例化,能够 实例化的只能是接口的实现类)。实例化接口类对象主要有两种方法:

1) 直接new 接口的实现类的实例,类似于
Map map=new HashMap(); //HashMap实现了Map接口
Interface1 instance=new ConcreteClass1();  //ConcreteClass1实现了Inteface1接口

  在一个API内部,这样写无可厚非,因为对调用者是屏蔽的,你今后将HashMap改为hashtable,不会影响到API调用者的程序。但是对于一个API调用的程序来说,这样就不太好了。如下面的调用(出租车公司增加一种高级轿车):
Car car=new BenzCar(); //BenzCar实现了Car接口
carRentCompany.addSuperCar(car);

  虽然可以这样,但这与面向接口的编程的原则不符,因为这要求你必须要知道接口的实现类,如果你有天想换一个实现类(如将奔驰车换成宝马车),就麻烦了,你必须找到你以前的应用程序源代码,将每一个调用接口实现类的地方都改过来,工作量比较大了。

  还有一种更好的方法,就是使用工厂。

2) 使用工厂,如
Car car=CarFactory.getSuperCar();
carRentCompany.addSuperCar(car);

  由工厂来提供实例化方法,你不必关心实例化类是哪一个。当然你必须知道工厂类是哪一个,更换接口实现的 时候,你只需要修改工厂的方法就可以了,其余的都不需要更改了。如上面的场景,将奔驰车换成宝马车,只要更改 CarFactory.getSuperCar()方法就可以了,用户调用API的程序不需要修改。

  举个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);
 
这里,DriverManager就是工厂,通过getConnection()方法来生产出数据库连接对象Connection, 用户使用Connect就可以实现JDBC编程了。

  但是,DriverManager是怎么知道使用的哪个数据库,使用哪个提供商的JDBC驱动来生产 Connectin的吗?答案在这里,原因就在Class.forName()的过程中,Class.forName()是将一个类初始化,这里,调用不 同厂商提供jdbc驱动时,使用了不同的jdbc驱动类的类名,该方法会初始化化该Driver类,该类在初始化时(static{}方法中)会初始化一 个自己的Driver类实例,并将实例注册到DriverManager中(的一个Vector中),再通过DriverManager中的 getConnection()方法找到Driver实例(通过jdbcurl),再调用Driver实例的getConnection()方法。 Driver实例也是一个生产Connection接口实例的工厂。DriverManager实际上是一个保存了各个jdbc驱动的一个缓存管理类,缓 存了各个驱动程序,并调用各个jdbc的驱动程序生产Connection对象。

  可以看出,只需要更改Driver的类名,提供不同的jdbcUrl和用户、密码,就可以生产出一个数据库连接(Connection接口类的实例)。
 
建议:如果你使用配置文件来提供工厂类的名字,程序通过调用配置文件来读取工厂的类名,更换接口实现就更简单了,你只需要改配置文件就可以了,而不需要更改程序。

接口和工厂之间关系形象比喻:

  接口就好比是产品规格,如螺母和螺钉的产品规格,规定了不同的大小尺寸,左旋等,工厂好比生产这些螺母 和螺钉的加工企业,而接口的实例好比不同工厂生成出的螺母、螺钉的产品。每一个不同的工厂虽然生成出的不同螺母和螺钉产品,但都必须遵守这个产品规格,这 样不同工厂生产出来的不同的螺母和螺钉才可以拧在一起使用,你也可以随意更换不同工厂生产出来的螺母或螺钉,而不会出现问题。

posted on 2008-05-05 10:16 gembin 阅读(564) 评论(0)  编辑  收藏 所属分类: JavaSE


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(6)

随笔分类(440)

随笔档案(378)

文章档案(6)

新闻档案(1)

相册

收藏夹(9)

Adobe

Android

AS3

Blog-Links

Build

Design Pattern

Eclipse

Favorite Links

Flickr

Game Dev

HBase

Identity Management

IT resources

JEE

Language

OpenID

OSGi

SOA

Version Control

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

free counters