JAVA在設計接口和類的規則時,有一個明確的規定。JAVA不支持類(實現)的多重繼承,但支持接口(定義)的多重繼承。
我已經無從了解這個設計的初衷,但這樣的規定隱含了以下的意義。
接口是設計的產物,即在需求設計時定義的對軟件功能的定義。而類是實現的產物,它是在實現過程中根據實現的具體情況而完成的。如果用代碼來說明就是:
在設計時我需要我設計的“模塊”提供兩個功能:
1.提供兩個整數相加的功能。
2.提供兩個字符串連接的功能。
我是一個偉大的設計師,為了不影響我的整體思路,我不會在這時停下來去實現它,所以我需要一個類似偽代碼的東西來記錄我的實現思路。java說,好,我們為你提供一個叫接口的東西,你只寫下你要實現的東西不用提供具體實現方法。
- interface IMyFunc {
-
- public int add(int a, int b);
-
- public String concat(String str1, String str1);
- }
。。。。。。。其它設計。
好了,哪個小匪來領實現這個任務?
小匪甲領取了這個編碼任務。分析大當家的設計思想,啊這個好辦。
- class MyMathImp implements IMyFunc{
-
- public int add(int n1,int n2) {
-
- //因為傳進來的都是整數,如果不是整數在編譯時就出錯了,
- //所以我不用檢查,直接的相加,哈哈,今天工資混到手了。
-
- return n1 + n2;
-
- }
-
-
-
- public String concat(String s1,Strings2) {
-
- //哎,這個,這個好象要檢查一下吧,如果有一個字符串是null,
- //我是顯示"字符串null"還是顯示"字符串"呢?如果兩個都為null,
- //我是顯示一個"null"還是顯示"nullnull"呢?
- //真復雜,嚴密性比具體實現還復雜,我干脆把它放到另一個方法中吧。
-
- return checkedConcat(s1,s2);
- }
-
- String checkedConcat (String s1,String s2){
- if(s1 == null && s2 == null) return "null";
- if(s1 == null) s1 = "null";
- if(s2 == null) s2 = "null";
- return s1 + s2;
- }
-
- }
checkedConcat我還想被別的地方調用,所以我不想設計成ptivate的。所以我設計的MyModuleImp從外面可以看到三個方法。
大當家的叫我提供兩個方法,而我的實現有三個方法,幸虧有接口啊,我發布這個模塊時只提供接口的文檔。這樣你在智能感應的IDE中一打開這個接口看到的只是大當家定義的兩個方法。
所以:接口可以只將設計意圖的方法暴露給調用者。
那么,當我一個類提供了不同功能的方法時,我想在不同時候只顯示某類功能的方法,怎么辦?
其實這個問題Sun的代碼中都沒有很好地處理。比如我的一個類提供了對象的初始化方法和對象釋放的方法,整數運算的方法和字符串運算方法以及圖形運算方法。當然這只是舉例。
有人說這個類設計不合理,這么多不同類型的功能應該在不同類中實現。
說得對!但是..........
如果我們的對象是本地生成的,多次new多個對象成本不是太高,如果我們的對象是從遠程調用的,那么多個對象的生成成本就很高了。
另外任何對象自身管理的方法和業務方法不能分到兩個類中實現吧?一個只有自身管理的方法的類除了自戀還有什么用?而有些必須要被始化和釋放等管理的 對象只有業務方法它也工作不起來,所以至少有兩類功能要在同一類中完成。假如它們的重載方法足夠多時,你會看到它們是很多功能的方法按字母排序混合在一 起。
典型的就是URLConnection類,你打開它時,設置請求參數的方法,獲取響應頭域的方法,獲取輸入輸出的方法一大堆。不過還好是它們的命名方式使很多功能相同的方法能排在一起。
如何有效的組織一個實現的不同功能的方法的分類?我們可以通過多接口來完成。
這個思想早在COM時代就已經采用了,無論你獲取的是IUnKnown接口還是IDispatch接口還是業務接口,其實反回的都是同一個 CoClass對象,但你獲取不同接口看到的是不同功能的函數。同時你只要實例化一次,獲取到任何接口就可以從這一接口生成其它接口,這對于調用者是非常 有意義的,我不僅能得到不同功能的函數分類,而且不需要多次實例化多個對象。
- interface IMath {
-
- public int add(int a, int b);
-
- public int mul(int a, int b);
- }
-
- interface IStrUtil {
-
- public String concat(String s1, String s2);
-
- public String Upper(String s1);
-
- }
-
- interface IObjManager {
-
- void init();
-
- void destory();
-
- }
-
- class MyModuleImp implements IMath, IStrUtil, IObjManager {
-
- public int add(int a, int b) {
- return a + b;
- }
-
- public int mul(int a, int b) {
- return a * b;
- }
-
- public String concat(String s1, String s2) {
- return checkedConcat(s1, s2);
- }
-
- public String Upper(String s1) {
- return checkedUpper(s1);
- }
-
- public void init() {
- System.out.println("init.........");
- }
-
- public void destory() {
- System.out.println("destory.........");
- }
-
- String checkedConcat(String s1, String s2) {
-
- if (s1 == null && s2 == null)
- return "null";
- if (s1 == null) s1 = "null";
- if (s2 == null) s2 = "null";
- return s1 + s2;
- }
-
- String checkedUpper(String s1) {
-
- if (s1 == null) return null;
- return s1.toUpperCase();
- }
- }
OK!
IObjManager om = new MyModuleImp(); //或getObjFromNet如果我們把生成實例的方法統一封裝起來,以及對象的生命周期管理都規定在一個類似IUnknown的接口中,我們就可以 把多功能的不同類有一個統一的管理行為,就象遠程EJB對象的實例化。
om. 這時我們看到的只是IObjManager接口的方法,我們可以init.destory.
當我們需要它的業務方法時:
IMath m = (IMath)om; //相當于QueryInterface
m. 這時我們看到的只是math相關的功能方法。
另外當我們想增加一個方法并保留原有方法時只需在新的接口中聲明,并讓實現類繼承自這個接口(其實就是在implements關鍵字后面加上接口名稱)。這樣新舊接口可以并存使用,既不影響以前的代碼,也不影響后來的人使用新接口:
- interface IMathEx{
-
- public int add(int a, int b);
-
- public int mul(int a, int b);
-
- public double sqrt(double a);
-
- }
-
-
-
- class MyModuleImp implements IMath,IMathEx, IStrUtil, IObjManager{
-
- //增加public double sqrt(double a)的實現
-
- }
這樣原來使用IMath來計算“加和乘“的代碼根本不受影響,而新的程序可以使用IMathEx來計算“加,乘和開方”運算。
所以多接口不僅使我們能夠有效的組織不同功能的代碼,而且可以使對象具有統一的管理方法,同時避免多次生成對象帶來的開銷。