隨筆-30  評論-123  文章-0  trackbacks-0
          摘要:雖然Java不允許多重繼承,但是有些情況卻允許其使用。本文將闡述在一個Web應用中模擬多重繼承的技術。

          在一般的開發中,Java的單繼承限制一般不會引起什么問題。實際上,需要使用多重繼承往往意味著糟糕的設計。然而還是存在一些情況,程序員們希望能夠繼承多個類。雖然Java不允許繼承多個類,但是有些技巧能夠模擬多重繼承。

          我在Swing應用程序和Web應用中都使用過這個技巧。Swing應用打包并部署服務到應用服務器上。這種情況下,因為我希望在不同組件間拖放對象而這些GUI組件共享同一個拖放方法,這樣的話,所有GUI組件需要擴展兩個類:GUI組件本身(JTree或JList)和通用的拖放類(drag-and-drop class)。本文中介紹的技術簡化了拖放類的實現。

          為了更好的闡述Java中多重繼承的技術,讓我們先來如何在一個Web應用中使用它。在這個Web應用中,Servlet類和其他類需要被擴展。我的這個應用很簡單,是一個基于文本的信息傳遞系統,能夠通過移動電話、PDA或者其他網絡終端設備傳遞信息給另一個移動電話。
          jw-1024-multiple.jpg
          如上圖所示,這個系統的核心是一個能夠從客戶端接受信息然后傳遞給移動電話的服務。為了簡化客戶端開發,我寫了一個MessageClient類來包含所有用于與服務通信的通用方法。因為這個類可以最為基類被所有可能的終端設備使用,因此能夠簡化客戶端開發。
          MessageClient類的源代碼如下:
          import?java.rmi.Naming;

          public?abstract?class?MessageClient
          {
          ???
          private?MessageServer?messageServer;

          ???
          public?MessageClient()
          ???
          {
          ??????System.out.println(
          "Initializing?Message?Client");
          ???}

          ???
          /**
          ????*?Method?used?to?connect?to?the?message?server
          ????*
          ????*?
          @param?serverName?name?of?the?server?that?contains?the?message?server
          ????
          */

          ???
          protected?void?connectToServer()
          ???
          {
          ??????String?serverName?
          =?getServerName();
          ??????
          try
          ??????
          {
          ?????????String?name?
          =?"//"?+?serverName?+?"/MessageServer";
          ?????????messageServer?
          =?((MessageServer)?Naming.lookup(name));
          ??????}

          ??????
          catch(Exception?e)
          ??????
          {
          ?????????System.out.println(
          "Error?connecting?to?Message?Server.??Exception?is?"?+?e);
          ?????????e.printStackTrace();
          ??????}

          ???}


          ???
          /**
          ????*?Method?used?to?send?message?to?server
          ????*
          ????*?
          @param?phoneNum?phone?number?to?send?message?to
          ????*?
          @param?message?message?to?send
          ????
          */

          ???
          public?boolean?sendMessage(String?phoneNum,?String?message)
          ???
          {
          ??????
          try
          ??????
          {
          ?????????
          return(messageServer.sendMessage(phoneNum,message));
          ??????}

          ??????
          catch(Exception?e)
          ??????
          {
          ?????????System.out.println(
          "Error?Sending?Message.??Exception?is?"?+?e);
          ?????????e.printStackTrace();
          ?????????
          return(false);
          ??????}

          ???}


          ???
          public?abstract?String?getServerName();
          }
          ?
          這個類包含3個方法:
          sendMessage()方法將實際信息發送到服務器;
          connectServer()方法負責連接服務。在本例中,服務是一個RMI服務;
          getServerName()是一個抽象類,因為不同設備決定服務名字的方式不同。也就是說,任何繼承自MessageClient的類都必須實現getServerName()方法。

          在開發與信息服務通信的客戶端程序時,我們需要多重繼承。我們的網絡客戶端是一個簡單的用來接收來自表單的信息并將其傳送到服務器的Servlet。為了完成上述任務,這個Servlet必須既繼承HttpServlet又繼承MessageClient。由于Java不允許這種繼承行為,我讓主類繼承HttpServlet,讓主類中的內部類繼承MessageClient,然后外部類創建了內部類的一個實例。代碼如下:
          public?class?SendMessageServlet?extends?HttpServlet{

          ???
          private?MessageClient?m_messageClient;
          ???
          private?String?m_serverName;

          ???
          public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)
          ?????????
          throws?ServletException,?IOException{
          ??????
          try{
          ????????
          //Get?server?name
          ?????????m_serverName?=?request.getServerName();
          ?????????System.out.println(
          "ServerName?is?"?+?m_serverName);
          ?????????
          //Create?message?client?to?communicate?with?message?server
          ?????????m_messageClient?=?new?ServletMessageClient();
          ?????????System.out.println(
          "Created?Message?Client");
          ?????????m_messageClient.connectToServer();

          ?????????
          //Get?message?and?phone?number
          ?????????String?phoneNum?=?(String)?request.getParameter("PhoneNum");
          ?????????String?message?
          =?(String)?request.getParameter("Message");

          ?????????
          //Send?message
          ?????????m_messageClient.sendMessage(phoneNum,message);

          ?????????
          //Display?page?to?tell?user?message?was?sent
          ?????????response.setContentType("text/html");
          ?????????RequestDispatcher?dispatcher?
          =?getServletContext().getRequestDispatcher("/SendMessageForm.jsp");
          ?????????dispatcher.include(request,?response);
          ??????}
          catch?(Exception?e){
          ?????????e.printStackTrace();
          ??????}

          ???}
          ?

          ???
          public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)
          ?????????
          throws?ServletException,?IOException???{
          ??????doGet(request,?response);
          ???}


          ????
          /**?Inner?class?used?to?extend?MessageClient?????*/
          ????
          public?class?ServletMessageClient?extends?MessageClient?{
          ???????
          public?ServletMessageClient(){
          ?????????
          super();
          ???????}

          ???????
          public?String?getServerName(){
          ??????????System.out.println(
          "Returning?ServerName?"?+?m_serverName);
          ??????????
          return(m_serverName);
          ???????}

          ????}

          }
          ?
          這種方法并不是真正的多重繼承,因為我們使用了代理(例如:MessageClient被很多外部類繼承,但并不是外部類本身),但是效果是一樣的。雖然MessageClient能被單獨的類擴展,但是使用內部類允許它訪問外部類中的所有成員和方法。這讓兩個類之間的交互變得很容易。

          這個例子只擴展了兩個類,你當然可以使用這種技術擴展任意多類。

          當然,其實這個信息傳遞服務的例子不用多重繼承也能實現。如果MessageClient有一個能夠接收服務服務名字的構造函數,那么getServerName()方法就可以不是抽象的。這意味著Web客戶端可以不必繼承MessageClient類。

          最后提醒開發者,只有在有明確的理由,確定要使用上面技術時才使用,并且使用時要小心。因為多重繼承使設計變得復雜,并且很容易被誤用。
          posted on 2006-05-16 11:40 學二的貓 閱讀(8356) 評論(6)  編輯  收藏 所屬分類: Java禪機

          評論:
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-16 12:53 | wolfsquare
          在這里似乎沒有多重繼承的必要.MessageClient已經能完成所有功能了.  回復  更多評論
            
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-16 13:02 | 學二的貓
          @wolfsquare
          我在文章的最后也提到過了,
          “當然,其實這個信息傳遞服務的例子不用多重繼承也能實現”。
          這么寫只是舉個例子,設計良好的Java應用絕對不會用到多重繼承。  回復  更多評論
            
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-16 17:55 | wolfsquare
          既然你把自己的心得寫出來,那么必然覺得它有所用途.
          既然它有所用途,那么自己就不會說出它沒有用的話,否則你說一個沒有用的東西出來有什么意義呢,基于想讓它有意義的理由,你的例子需要再舉得合適一些。
            回復  更多評論
            
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-16 18:54 | 學二的貓
          @wolfsquare
          模擬多重繼承的這個技術是為解決一個實際項目中的問題而產生的,當然有他的實際意義。

          實際項目中由于一開始的設計存在問題,導致我們后期開發時不得不引入多重繼承機制,因為這么做付出的代價最小。我知道最好的解決方案當然是重構這個工程,或者修改一大堆的接口和類,然后重新測試。同志們,如果你是項目經理,你會這么做嗎??

          項目是以成敗論英雄的,做項目最講究用最小的投入達成既定目標。在我的項目中,多重繼承這個解決方案付出的代價最小,盡管還存在其他許多解決方法,但是我們最后還是決定保留原來不好的設計,采用多重繼承解決問題。

          好與壞之間沒有嚴格的界限,同一樣東西放到不同環境中,效果和評價是不一樣的。就像我在文章開頭說的一樣:“在一般的開發中,Java的單繼承限制一般不會引起什么問題。實際上,需要使用多重繼承往往意味著糟糕的設計。”但是在我的這個項目中,多重繼承確實最佳解決方案。

          我很抱歉給出的例子可能不是很恰當,因為這是我自己編的一個例子,目的是讓例子更好懂,如果我把項目里的代碼拿出來,是的,那確實很恰當,但是那樣做的話會涉及到21個類3個接口,類里面最小的也有60句,多的有2400句代碼,我這樣貼出來,一我想我說不明白事情了--實際問題確實太復雜,二恐怕要花費你很多寶貴的時間,更糟糕的是,你還不一定能看明白。并且我也違反了公司的竟業守則和保密協議。

          請原諒我一時間確實找不出更好的例子,因為需要用到多重繼承的情況極少極少,只在某些特殊情況下才會選擇使用多重繼承來解決問題(例如我的這個項目,使用多重繼承代價最小)。我寫這篇文章的目的是想告訴大家Java存在這么一種技巧可以模擬多重繼承,如果有一天,你的項目碰到了和我類似的情況,你就可以不必重構你的項目或者對項目大刀闊斧了,因為有一種在某些特定情況下很有用的技巧--模擬多重繼承。  回復  更多評論
            
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-16 20:58 | wolfsquare
          由于在本例中你舉例的場景不太合適,本來是解惑的東西卻讓人更迷惑甚至誤導,豈不是更糟?
          期待你的改進版本.  回復  更多評論
            
          # re: 在Java中模擬多重繼承--擴展多個類的技巧 2006-05-17 08:50 | 學二的貓
          @wolfsquare
          我的文章讓你感到迷惑了?那是我寫得不好。令大家看不懂或者模棱兩可決不是我的本意。

          文中的例子是實際項目的簡化,目的是能用簡單的代碼說明如何模擬多重繼承。

          我想看了這篇文章你應該會對如何實現模擬實現多重繼承這個技巧本身有所領悟,至于這個技巧的應用時機,我確實很難用一個短小的例子恰當的呈現出來--因為如果一個小例子需要用到多重繼承,我完全可以重構它讓他不用多重繼承就可以實現,因為重構的代價并不大。

          技巧本身我想大家都看得懂、學得會,但是有沒有機會使用,就要看你實際開發中能不能碰到像我一樣類似得情況或更復雜得情況了。

          如果你連模擬多重繼承這個技巧本身也沒有看懂,請您告訴我,我會重新思考,重寫本篇文章。因為寫這篇文章的目的只是想教大家這么一個技術,應用時機還得大家從實踐中去體會。  回復  更多評論
            
          主站蜘蛛池模板: 定安县| 建德市| 福贡县| 阳东县| 同仁县| 和龙市| 夹江县| 博客| 乐东| 临高县| 达孜县| 济源市| 昂仁县| 龙海市| 柳林县| 陆丰市| 泽州县| 布拖县| 普陀区| 册亨县| 三都| 寿阳县| 武宁县| 建始县| 四会市| 五峰| 台东县| 成武县| 崇阳县| 扶沟县| 泗水县| 柘城县| 西平县| 宁乡县| 平江县| 浦城县| 大埔区| 孟津县| 深州市| 海丰县| 霍邱县|