如何設計一個易用的MVC架構

來源:互聯網
上載者:User

標籤:des   style   class   blog   code   java   

導言

把一件簡單的事情做複雜很容易,把一件複雜的事情做簡單卻不易。在電腦的世界裡,

馮.諾依曼把複雜的電腦簡化為:儲存空間,控制器,運算器和I/O裝置;

丹尼斯·裡奇把晦澀的組合語言簡化為258頁的《C程式設計語言》;

詹姆斯高斯林把繁瑣的跨平台編碼簡化為256條位元組碼指令;

對我們大部分人而言,把簡單的事情做簡單就足夠了。

關於架構

架構是對某一類共通業務的封裝,架構設計應該遵循幾個基本的原則:1 易用性 2 穩定性3 擴充性,架構從來都是給別人用

的,架構的學習成本與他的複雜度成正比,如果你設計了一個功能強大但介面複雜的架構,我想,沒有幾人願意使用;同樣,

穩定性也是必須的,穩定的架構體現設計者所遵循的理念,穩定的架構也會越來越成熟;擴充性是架構的靈魂,沒有擴充性

架構只會像明朝的八股文,禁錮才華橫溢的書生,業務總是在變化,架構必須具備適應這種變化的能力。

java web架構發展,大致經曆以下幾個階段:

刀耕火種時期

早期的java web技術採用serverlt實現,與CGI類似,只不過serverlet採用的是多線程而不是多進程,服務端除了處理商務邏輯之外

還負責頁面的組織,通過Printwirter 列印html標籤,這給當時的開發人員帶來了無盡的麻煩,為了修改一個CSS樣式或者一段javacscript

不得不重新編譯serverlet,為了輸出簡單的處理結果,你不得不用大量的篇幅列印html標籤,下面是一個簡單不過的例子:

import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SimpleServlet extends HttpServlet {        private static final long serialVersionUID = 1L;    public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {        response.setContentType("text/html");        PrintWriter out = response.getWriter();        String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n";        out.println(docType + "<HTML>\n" +        "<HEAD><TITLE>Hello</TITLE></HEAD>\n"         + "<BODY BGCOLOR=\"#FDF5E6\">\n" +         "<H1>Hello world</H1>\n" + "</BODY></HTML>");        out.flush();        out.close();    }}

這段代碼唯一的功能就是輸出helloworld。很難想象,在複雜的業務下,程式員要在這種重複的功能上耗費多少時間,這個時期程

序員就像新石器時代的人民,用最原始的工具拓荒,當然這個時期的web還談不上什麼架構。當時的sun公司也注意到這點,隨後

推出了JSP,java 進入下一個時代。

農業文明時期

為瞭解決用serverlet遇到的諸多問題,sun在1999年推出JSP技術,JSP允許在頁面上插入java代碼進行邏輯處理,同時也提供

了大量的tag處理html標籤,大大提高了開發效率,開發人員從繁瑣的out.prinlt中解脫出來,用更多的精力去關注商務邏輯,在此

基礎上演化出jsp+Serverlet兩層架構和 jsp+serverlet+javabean三層架構,在這種架構下,jsp側重頁面展現,serverlet負責業

務邏輯,javabean則負責二者的資料傳遞,這就是MVC的雛形,後續web架構也大多基於這種模式來發展,有的關注其中的某層,如

FreeMarker,有點關注整個流程,如struct。這個階段的程式員開發逐漸邁入固定模式,像農業文明時代一樣,使用著有限的工具,

推動著社會的發展。隨著類似架構的不斷使用和發展,java web邁入另一個跨域式發展的時期。

工業文明時期

 就像工業革命標誌工業文明的發展一樣,java web的工業文明時代同樣有著標誌性的事件,那就是2000年5月發布的structs架構,

這個架構標誌著機械化開發的到來,在採用structs後,我們按照固定的模式寫著類似的代碼,配類似的配置,如果你接手過這個時期

的項目,你會發現他們都有大致的結構,這是一種跨越式的發展,20%的市場佔有率也說明當時的程式員對這種架構有多麼的渴求。

同時structs也降低了程式員的要求,找一個新手進行簡單的培訓後就可以進行項目開發,穩定的架構下開發也減少了出錯的可能,

大大降低了項目的開發和維護成本。人們在使用中和思考中不斷修複和革新這些架構,許多優秀的想法不斷萌現,架構也不斷智能化,

引領廣大程式員邁入下一個新紀元。

現代文明時期

現在文明追求智能化讓人類從繁重的體力中解放出來,享受生活,同樣,跨入現代文明的web 開發也追求程式員的解放,大量

優秀的架構湧現:webwork  、Tapestry 、echo、spring等等不甚枚舉,這些架構都和優秀,通過簡單的配置就你實現強大

的功能,但要從中選擇一個標誌性,那麼我將毫不猶豫選擇spring,如果說java讓程式員從作業系統的束縛中解脫出來,那麼

spring就是讓程式員從複雜的設計模式中解脫出來,IOC 、DI、 AOP、 RMI、Proxy,天才Rod Johnson一個不落的包含進來,

為java界帶來新的春天,優秀的思想加上簡單的API成就Spring無可替代的地位。

重複造車輪

討論了這麼多架構和設計,似乎跟我們的標題沒有多大關係,而且有這麼多優秀的架構,我們有必要自己設計嗎?答案是肯定的,

社會發展的動力是革新,而革新的動力是對現有的事物不斷思考,如果都安於現狀,那麼就無進步可言,也許大多數人都認為

web就應該按MVC設計,編碼,當然,我也認同這點,但是隨著對這些架構的應用,讓我不禁想到許多問題,帶著這些問題,

有了一個重複造車輪的想法。帶著對structs的學習,我們也自己實現也該“MVC”架構。 struct很優秀,他主要實現了http

請求道使用者邏輯的封裝,事實上serverlet已經實現了這一點,不過serverlet只能做到將請求映射到一個具體的類,只提供了

service和doPost doGet之類的幾個簡單介面處理使用者請求,而實際的開發中,在面臨千差萬別的業務時,我們不得不在

serverlet裡加大量的if else判斷:

public class SimpleServlet extends BaseServlet {    private static final long serialVersionUID = 1L;    @Override    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        super.service(request, response);        String command = request.getParameter("command");        if ("query".equals(command)) {            //doXX        } else if ("add".equals(command)) {            //doXX        } else if ("delete".equals(command)) {            //doXX        } else if ("update".equals(command)) {            //doXX        }else{            //doXX        }     }}

增加一個業務,你就得增加一個If-else,寫類似的代碼,後期的維護會讓你頭痛不已,當你要修改某個分支的功能時,你不得

不把所有的分支用例都跑一遍,確保對其他邏輯沒有影響,實際上,這段代碼有個共通的邏輯:字典尋找功能,根據請求參數找到

對用的處理函數,這就是典型的表驅動模式,也叫表尋找:事先註冊相應的處理邏輯,在需要使用這些邏輯時,給出邏輯的關鍵字

找到對應的實現,當你的代碼中充斥大量if-else或switch-case,你就應該考慮這種方式,讓我們用表驅動來重構以上代碼。

表驅動有三大元素:表 ,介面,管理類。

我們先提取一個簡單的介面:

package com.czp.opensource.mvc.service;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** *  * @description <p> *              服務介面,每個實作類別處理相應的請求 *              </p> * @author Czp * @version 1.0(2014-6-16) *  */public interface ServiceItf {    /**     *      * @desc: <p>     *        處理http請求     *        </p>     * @param request     * @param respose     */    public void process(HttpServletRequest request, HttpServletResponse respose);        /**     *      * @desc:     *     <p>處理的業務類型</p>      * @return     */    public String processType();}

可以看到,這個介面並沒有什麼特別的,只是HttpServerlet的簡單封裝,但是他為我們下一步的封裝提供了可能。

接下來我們提供一個管理類,管理介面的實作類別,包括註冊和尋找:

package com.czp.opensource.mvc.service;import java.util.concurrent.ConcurrentHashMap;/** *  * @description <p> *              服務工廠:負責註冊 刪除 尋找服務,在整個應用的生命 *              周期類只需要一個工廠,所以實現為單例 *              </p> * @author Czp * @version 1.0(2014-6-16) * @since JDK1.5+ *  */public class ServiceFactory {    private volatile static ServiceFactory INSTANCE;        private ConcurrentHashMap<String, ServiceItf> services;        private ServiceFactory(){        services = new ConcurrentHashMap<String, ServiceItf>();    }        /**     *      * @desc:     *     <p>     *         Double check lock方式延遲初始化     *         只能在JDK1.5+調用     *     </p>      * @return     *    INSTANCE     */    public ServiceFactory getInstance(){        if(INSTANCE==null)        {            synchronized (ServiceFactory.class) {               if(INSTANCE==null)               {                   INSTANCE = new ServiceFactory();               }            }        }        return INSTANCE;    }        /**     *      * @desc:     *     <p>註冊處理器</p>      * @param service     * @return     */    public boolean regist(ServiceItf service)    {        ServiceItf putIfAbsent = services.putIfAbsent(service.processType(), service);        return putIfAbsent!=null;    }        /**     *      * @desc:     *     <p>根據業務類型尋找處理器</p>      * @param proccessType     * @return     */    public ServiceItf getService(String proccessType)    {        return services.get(proccessType);    }    /**     *      * @desc:     *     <p>清空表</p>     */    public void onClose(){        services.clear();        services = null;    }}

 接下來我們需要在合適的時間點註冊我們的實作類別,這個操作應該在所有請求到達前,最好的選擇就是在web容器初始化的時,

因此,我們選擇在listener裡註冊

package com.czp.opensource.mvc.service;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;/** *  * @description *       <p>上下文環境,負責初始化MVC架構的上下文</p> * @author  *        Czp * @version  *        1.0(2014-6-16) * */public class ContextListener implements ServletContextListener {        @Override    public void contextDestroyed(ServletContextEvent arg0) {        ServiceFactory.getInstance().onClose();    }    @Override    public void contextInitialized(ServletContextEvent arg0) {        ServiceFactory instance = ServiceFactory.getInstance();        instance.regist(new LoginService());        //註冊其他服務,在此不贅述        //instance.regist(new LoginService());    }}

接下來我們就可以用這個簡易的架構重構SimpleServlet

package com.czp.opensource.mvc.service;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SimpleServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    @Override    protected void service(HttpServletRequest request,            HttpServletResponse response) throws ServletException, IOException {        String command = request.getParameter("command");        ServiceItf service = ServiceFactory.getInstance().getService(command);        service.process(request, response);    }}

代碼從我們的6個if-else簡化為短短3行代碼,最重要的是,增強了健將性,可維護性和擴充性,當你需要新加任何業務時,

無需要修改和測試原來的邏輯。以上就是spring-mvc struct等所有的MVC架構的本質所在,只不過他們的service是通過xml配置。

 

這個簡單的架構存在許多問題,如果你也發現了,請留言指出,下篇我們將進一步完善這個架構。 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.