CHAPTER 2 MVC 基本原理
如果你已經充分了解Internet,你或許會像我一樣,準備開始傳統的軟件開發。在此之前,我們最好先學一下方法學,以便為今后的開發節約更多的時間。
不同的開發人員(比如頁面設計者和代碼開發者)完成不同的開發任務,盡量避免交叉,這樣做可以起到事半功倍的效果。
在這一節,我們將公正的深入討論一下混雜式編程,同時介紹MVC方法學,并介紹MVC是如何解決混雜式編程問題的。
混合呈現和邏輯
在此,我們創建了一個嵌入數據庫調用腳本的WEB頁面,并對它進行分析。
<%@ page language='java' import='java.sql.*' %>
<HTML>
<HEAD>
<TITLE>Test</TITLE>
</HEAD>
<%
ResultSet rs;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(
"jdbc:mysql://localhost/products");
statement = connection.createStatement();
%>
<BODY>
<%
if (request.getParameter("submit") = "submit") {
rs = statement.executeQuery("SELECT username, password,
city, state FROM product where username = " +
request.getParameter("username"));
if (rs.next()) {
%>
<form action="test.jsp" method="post">
<input type="text" name="username"
value="<%= rs.getString("username") %>">
<input type="text" name="password"
value="<%= rs.getString("password") %>">
<input type="text" name="city"
value="<%= rs.getString("city") %>">
<input type="text" name="state"
value="<%= rs.getString("state") %>">
<input type="submit" name="submit" value="Update">
</form>
<% } else { %>
No Information
<% }
}
%>
else if (request.getParameter("submit") = "update") {
statement.executeUpdate("UPDATE product set password= '"
+ request.getParameter("password") + "'" +
", city = '" + request.getParameter("city") + "'" +
", state = '" + request.getParameter("state") + "'" +
"where username = '" +
request.getParameter("username"));
}
} catch(ClassNotFoundException e) {
out.println("Driver Error");
} catch(SQLException e) {
out.println("SQLException: " + e.getMessage());
}
%>
<form action="test.jsp" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="text" name="city">
<input type="text" name="state">
<input type="submit" name="submit" value="Submit">
</form>
</BODY>
</HTML>
Listing 2.1 A mixed presentation logic page.
以上代碼是JSP頁面的一個混合VIEW呈現和邏輯很好的例子,讓我們來考慮以下幾個在使用這個頁面時必須面對的問題。
作為一個開發者,假設公司讓你去創建一個簡單的HTML窗體,以接收或顯示用戶名細節情況。這個需求的改變將導致你需要從復雜的頁面代碼中分離出商業邏輯邏輯,并在適應的位置增加新邏輯。
當然,并非所有的頁面都這么簡單,有些時候,需要在頁面中增加一些圖片時。對代碼開發者來說,由于他對界面設計不太了解,最終結果將導致頁面非常難看;對頁面設計者來說,由于他不了解JAVA代碼,最終可能出現程序不能運行的結果。
最后的結果就頁面將難以維護和繼續。
我希望這個故事對你來講最好不要成為現實,但現實的情況恬恬如此。
Smalltalk-80 MVC三合一架構
當Smalltalk-80被大量使用的時候,一種用于分離顯示和邏輯的方法就顯得非常重要了。進行分離后,前臺只負責顯示任務,后臺負責數據獲取和格式化處理,各司其職。下圖為Smalltalk-80 MVC三合一架構。
Figure 2.1 The Smalltalk-80 MVC triad.
In Figure 2.1,我們發現它有三個主要組件:model, view和controller。下面將對這三者和它們之間的關系進行解釋。
Model
MVC的模型部分通常由以下兩部分組成:
■類或者別的數據結構,用于呈現系統或應用程序狀態
■動作/方法,用于改變系統狀態
在很多情況下,模型用于呈現數據庫里的數據。如果你在JAVA中使用MVC,則模型將被創建為JavaBeans,用于訪問或加載系統數據。
使用模型的最核心想法是為了從用戶呈現頁面中完全分離數據。意思是模型與所有的輸入和輸入無關。可以從VIEW和控制器中訪問模型。控制器從用戶窗體接收輸入信息,當信息需要進行加工時,控制器與模型進行交互,以得到適當的數據。
為了顯示模型的信息,VIEW組件在模型里注冊自己。當信息發生改變時,模型通知所有注冊了的VIEW,告訴他們信息已經改變,可以獲取新的信息并呈現給用戶。模型不限于一個VIEW,它可以允許多個VIEW進行注冊,并向他們告知系統目前的狀態。
Figure 2.2 Model inputs and outputs.
正如你在Figure 2.2里看到的一樣,模型主要連接到數據源(可以是數據庫、固定格式的文件或一些擴展接口)。模型負責維護數據的真實性和可用性和完整性。其他輸入包括VIEW組件更新注冊和數據更新通知。
View
在我們討論模型的時候已經接觸到了VIEW。VIEW組件是一個可視化組件,它用于向用戶顯示由模型建立的數據。在多數情況下,VIEW被設計成幾種格式:比如字符、簡單列表或一個組合風格。
當用戶需要使用模型中的信息時,應用將生成一個新的用于某種特定格式顯示數據的VIEW組件,VIEW將在模型里自動注冊,以便于在數據或狀態發生改變時,模型好通知該生成的VIEW組件。VIEW是如何獲取數據的呢?其實,他可以通過調用模型中的特定方法,或接受串行化數據對象。VIEW也可以從控制器中獲取數據(下一節,我們將討論這個問題)。
Controller
控制器組件負責處理所有在應用和用戶之間的交互。所有從鍵盤、鼠標和其他擴展接口的輸入都將被發送到控制器組件。使用預先定義的邏輯,控制器確定數據是否需要在模型里更新或是否需要創建一個新的VIEW組件。
正如你所了解的一樣,一些商業邏輯都包含在控制器和模型里,在VIEW組件里不包含商業邏輯。
MVC架構
MVC架構在Gang的四本書里都有涉及(Design Patterns: Elements of Reusable Object-Oriented Software)。在高級語言里建立MVC觀念需要做很多工作,比如:JAVA包括兩個有個的接口,java.util.Observable 和 java.util.Observer,用于監測和通知狀態改變。在剩余章節里,我們主要關注MVC在WEB開發中相關的內容。
Sun模式1 和 2
之前,我們討論了Smalltalk-80的MVC定義,在WEB開發的開初,用于快速開發的新技術不斷產生。JSP,一個類似的技術,允許WEB頁面從靜態內容向動態內容轉變。把JSP用于MVC的模式1時,很不幸,除非你非常小心,一個動態WEB頁面看起來像Listing 2.1一樣,并且有太多的VIEW和控制器代碼混雜在里面。
隨著servlet的發展,開發者可以把控制器代碼建立在動態頁面里,并在里面放置自己的組件。這時,SUN提出了模式2,因為在MVC架構里有2個組件被使用。但許多可能需要考慮,其中之一就是模式2和MVC之間如果聯系,許多典型的原因就是WEB應用無法利用Observer(監測)模式。
將MVC擴展到WEB應用
WEB站點利用HTTP協議進行數據傳遞。當用戶訪問WEB站點時,單擊按鈕或鏈接將向WEB服務器發送一個GET 或 POST HTTP協議請求。然后,WEB服務器處理這個請求并返回HTML頁面給用戶。如Figure 2.3所示
Figure 2.3 Web page processing.
正如Figure 2.3顯示的一樣,整個處理過程是線性的,所有信息被轉換附加到請求上。事實上,在Figure 2.3顯示的處理中,WEB頁面直接訪問數據,從VIEW到數據層之間沒有任何中介處理層,這將導致許多問題,這不是一個好的處理辦法。解決方法是,在處理層加入適當的層,把它們進行分離。如Figure 2.4所示的三層結構。
Figure 2.4 Three-tier Web page processing.
在Figure 2.4里,我們嘗試把MVC架構帶入WEB開發中,以分離不同層之間的功能。
MVC實踐
在學習了以上知識的基礎上,讓我們看一下如何把Listing 2.1中的代碼轉換為MVC方式,讓WEB設計者和WEB開發者的工作更容易。首先,我們需要創建一個包含所有顯示信息的VIEW。如Listing 2.2所示:
<HTML>
<HEAD>
<TITLE>Test</TITLE>
</HEAD>
<BODY>
<form action="ControllerServlet.jsp" method="post">
<input type="text" name="username"
value="$$username">
<input type="text" name="password"
value="$$password">
<input type="text" name="city"
value="$$city">
<input type="text" name="state"
value="$$state">
$if ($$first) {
<input type="submit" name="submit" value="Submit">
$else
<input type="submit" name="submit" value="Update">
</form>
</BODY>
</HTML>
Listing 2.2 Example view code.
正如你在Listing 2.2里看到的一樣,我們仍舊需要WEB設計者擁有一些通過控制器操作MODEL模型里數據的能力。根據個人經驗,在VIEW層使用Loop和條件將減輕WEB應用開發相當多的工作。這些代碼不能被用戶直接訪問,在顯示給用戶之前, WEB服務器將把它當作模板進行處理。
處理來自VIEW的請求,需要創建一個servlet(JAVA),詳見Listing 2.3
public class ControllerServet extends HttpServlet {
private AccountLocalHome home = null;
public void init() throws ServletException {
try {
Context cmp = (Context) new
InitialContext().lookup("java:comp/env/cmp");
home = (AccountLocalHome) cmp.lookup("AccountBean");
} catch (NamingException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
if (home == null) {
out.println("home is null");
} else {
AccountLocal account = home.create();
ServletContext app = getServletContext();
app.setAttribute("username", account.username);
app.setAttribute("passowrd", account.password);
app.setAttribute("city", account.city);
app.setAttribute("state", account.state);
RequestDispatcher disp;
disp = app.getRequestDispatcher("/ViewAccount.tmp");
disp.forward(request, response);
}
}
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
doGet(req, res);
}
}
Listing 2.3 Example servlet code.
這個Servlet可以使用EJBs作為MODEL模型組件訪問數據。在很多情況下,控制器Servlet將為特定的商業功能處理其他Servlet或對象。我們的Servlet將獲得一個Account對象和對象與當前WEB應用的上下文之間的聯系。這個處理將把存儲在上下文中的數據傳送到模板代碼中(Listing 2.2)。
本章小節和下章介紹
在這一章里,我們探索了一MVC架構(一個用于分離系統組件和商業邏輯的方法)。最突出的問題是在VIEW組件里應該使用什么語言。我們的解決方案是使用Velocity——一個為WEB設計者特制的用于處理信息顯示的語言。在下一章里,我們將介紹Velocity,分析其結構,并且用示例說明如何把Velocity集成到MVC架構里。
※※譯者注:MVC是一種方法學,而不是一種特定的語言框架※※
posted on 2008-10-11 22:51 KINGWEE 閱讀(570) 評論(0) 編輯 收藏 所屬分類: Velocity