在Java API設計中,面向接口編程的思想,以及接口和工廠的關系


現在的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接口類的實例)。

 

  建議:如果你使用配置文件來提供工廠類的名字,程序通過調用配置文件來讀取工廠的類名,更換接口實現就更簡單了,你只需要改配置文件就可以了,而不需要更改程序。

接口和工廠之間關系形象比喻:

  接口就好比是產品規格,如螺母和螺釘的產品規格,規定了不同的大小尺寸,左旋等,工廠好比生產這些螺母和螺釘的加工企業,而接口的實例好比不同工廠生成出的螺母、螺釘的產品。每一個不同的工廠雖然生成出的不同螺母和螺釘產品,但都必須遵守這個產品規格,這樣不同工廠生產出來的不同的螺母和螺釘才可以擰在一起使用,你也可以隨意更換不同工廠生產出來的螺母或螺釘,而不會出現問題。

http://blog.sina.com.cn/s/blog_6f505d710100mxsy.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM