摘要:
雖然Java不允許多重繼承,但是有些情況卻允許其使用。本文將闡述在一個Web應用中類比多重繼承的技術。
在一般的開發中,Java的單繼承限制一般不會引起什麼問題。實際上,需要使用多重繼承往往意味著糟糕的設計。然而還是存在一些情況,程式員們希望能夠繼承多個類。雖然Java不允許繼承多個類,但是有些技巧能夠類比多重繼承。
我
在Swing應用程式和Web應用中都使用過這個技巧。Swing應用打包並部署服務到應用伺服器上。這種情況下,因為我希望在不同組件間拖放對象而這些
GUI組件共用同一個拖放方法,這樣的話,所有GUI組件需要擴充兩個類:GUI組件本身(JTree或JList)和通用的拖放類(drag-and-
drop class)。本文中介紹的技術簡化了拖放類的實現。
為了更好的闡述Java中多重繼承的技術,讓我們先來如何在一個Web應用中使用它。在這個Web應用中,Servlet類和其他類需要被擴充。我的這個應用很簡單,是一個基於文本的資訊傳遞系統,能夠通過行動電話、PDA或者其他網路終端裝置傳遞資訊給另一個行動電話。
如所示,這個系統的核心是一個能夠從用戶端接受資訊然後傳遞給行動電話的服務。為了簡化用戶端開發,我寫了一個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類。
最後提醒開發人員,只有在有明確的理由,確定要使用上面技術時才使用,並且使用時要小心。因為多重繼承使設計變得複雜,並且很容易被誤用。