随笔-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 学二的猫 阅读(8332) 评论(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
我的文章让你感到迷惑了?那是我写得不好。令大家看不懂或者模棱两可决不是我的本意。

文中的例子是实际项目的简化,目的是能用简单的代码说明如何模拟多重继承。

我想看了这篇文章你应该会对如何实现模拟实现多重继承这个技巧本身有所领悟,至于这个技巧的应用时机,我确实很难用一个短小的例子恰当的呈现出来--因为如果一个小例子需要用到多重继承,我完全可以重构它让他不用多重继承就可以实现,因为重构的代价并不大。

技巧本身我想大家都看得懂、学得会,但是有没有机会使用,就要看你实际开发中能不能碰到像我一样类似得情况或更复杂得情况了。

如果你连模拟多重继承这个技巧本身也没有看懂,请您告诉我,我会重新思考,重写本篇文章。因为写这篇文章的目的只是想教大家这么一个技术,应用时机还得大家从实践中去体会。  回复  更多评论
  

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


网站导航: