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