1. 程式人生 > >從Filter看責任鏈模式

從Filter看責任鏈模式

模擬請求

一個Request類來模擬請求物件、一個Response類來模擬響應物件、一個APP類用來模擬請求處理的過程,可以認為是Servlet請求處理的一個簡版

package com.seven.filter;

public class Request {

    /**
     * 請求資料
     */
    private String data;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
package com.seven.filter;


public class Response {

    /**
     * 響應資料
     */
    private String data;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
package com.seven.filter;

public class App {

    public
static void main(String[] args) { Request request = new Request(); Response response = process(request); System.out.println(response.getData()); } /** * 模擬請求處理全過程 * * @param request 請求物件【模擬HttpServletRequest物件】 * @return Response 返回物件【模擬HttpServletResponse物件】 */
private static Response process(Request request) { Response response = new Response(); response.setData("處理成功"); return response; } }

新增功能

  1. 新增引數校驗,判斷請求引數中是否包含test字串,不包含的話直接處理失敗
package com.seven.filter;

public class App {

    public static void main(String[] args) {

        Request request = new Request();
        Response response = process(request);
        System.out.println(response.getData());
    }


    /**
     * 模擬請求處理全過程
     *
     * @param request 請求物件【模擬HttpServletRequest物件】
     * @return Response 返回物件【模擬HttpServletResponse物件】
     */
    private static Response process(Request request) {
        Response response = new Response();
        if (request != null && request.getData() != null && request.getData().contains("test")) {
            response.setData("處理成功");
        }
        response.setData("處理失敗");
        return response;
    }

}
  1. 新增日誌功能,在請求處理前後新增日誌
package com.seven.filter;

public class App {

    public static void main(String[] args) {

        Request request = new Request();
        Response response = process(request);

    }


    /**
     * 模擬請求處理全過程
     *
     * @param request 請求物件【模擬HttpServletRequest物件】
     * @return Response 返回物件【模擬HttpServletResponse物件】
     */
    private static Response process(Request request) {
        Response response = new Response();
        if (request != null && request.getData() != null && request.getData().contains("test")) {
            System.out.println("————————開始處理——————————");//模擬日誌
            response.setData("處理成功");
            System.out.println("————————處理結束——————————");//模擬日誌
        }
        response.setData("處理失敗");
        return response;
    }

}
缺點
  1. 每次新加功能需要改動主方法,違反開閉原則,不易擴充套件
  2. 新增的引數校驗、日誌都不是主要業務邏輯,而卻和主要業務程式碼耦合在一起,經常改動,風險較大

Filter 模式

先回憶下我們使用Filter的情況,Filter是針對請求的,一般是在請求處理前進行攔截,做一些比如統一編碼、許可權校驗、日誌記錄等功能,我們下面就模擬許可權校驗(這裡用引數檢驗代替)、日誌記錄這兩項功能,使用Filter模式來模擬上面的功能實現,同時根據FilterChain,我們體會下責任鏈模式

package com.seven.filter;

public interface Filter {

    /**
     * 過濾器
     * @param request 請求物件
     * @param response 返回物件
     */
     void doFilter(Request request, Response response,FilterChain chain);
}
package com.seven.filter;

/**
 * 引數校驗過濾器
 */
public class ParameterValidateFilter implements Filter {

    @Override
    public void doFilter(Request request, Response response,FilterChain chain) {

        if (request != null && request.getData() != null && request.getData().contains("test")) {
            chain.doFilter(request,response,chain);
        }else {
            response.setData("處理失敗");
        }
    }
}

package com.seven.filter;

/**
 * 日誌過濾器
 */
public class LogFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        System.out.println("————————開始處理——————————");//模擬日誌
        chain.doFilter(request, response, chain);
        System.out.println("————————處理結束——————————");//模擬日誌
    }
}

package com.seven.filter;

import java.util.ArrayList;
import java.util.List;

/**
 * 過濾器鏈【執行責任鏈模式的主要成員】
 */
public class FilterChain  implements Filter{

    /**
     * 過濾器列表
     */
    private List<Filter> filters = new ArrayList<>();

    private int index=0;

    /**
     * 新增過濾器
     * @param filter
     */
    public void addFilter(Filter filter) {
        filters.add(filter);
    }

    @Override
    public void doFilter(Request request, Response response,FilterChain chain) {
        if(index==filters.size()){
            /**
             * 真正處理請求
             */
            response.setData("處理成功");
            return;
        }
        Filter filter = filters.get(index);
        index++;
        filter.doFilter(request,response,chain);
    }
}
package com.seven.filter;

public class App {

    public static void main(String[] args) {
        Request request = new Request();
        request.setData("test");
        process(request);

    }


    /**
     * 模擬請求處理全過程
     *
     * @param request 請求物件【模擬HttpServletRequest物件】
     * @return Response 返回物件【模擬HttpServletResponse物件】
     */
    private static Response process(Request request) {
        Response response = new Response();
        FilterChain chain = new FilterChain();
        chain.addFilter(new ParameterValidateFilter());
        chain.addFilter(new LogFilter());
        chain.doFilter(request, response, chain);
        return response;
    }

}
————————開始處理——————————
處理成功
————————處理結束——————————

總結

  1. Filter是針對請求進行攔截、意在請求前進行一些過濾、許可權校驗、日誌記錄、統一編碼等
  2. 所有Filter統一在FilterChain中組成一個鏈條,然後呼叫也統一由FilterChain來協調,確保以鏈條的模式執行
  3. 責任鏈模式是一種重要的設計模式,如Servlet中的Filter模式、Mybatis中的Plugin模式等都是責任鏈模式的體現

圖注:責任鏈模式

微信公眾號:宋坤明
下面的是我的公眾號二維碼圖片,歡迎關注。
圖注:宋坤明公眾號