淺談 Facade 模式

來源:互聯網
上載者:User

Facade 模式

所謂 Facade 模式,是一個可以讓事情變得有點頭緒的好東西。

 

一個 Facade 肯定是一位某方面的“行家”,例如資料庫操作。它對來自上層的請求屏蔽了具體的商務邏輯細節,任何程式需要對資料庫進行 CRUD 操作時,只需要告訴 Facade 層“我要做什麼”,而 Facade 層則知道“到哪裡去做”,於是它根據請求的具體內容,調用相應的底層模組(在那裡解決“到底怎樣做”的問題)。進行具體的操作。當它從底層模組中取得所需的資料後,再將結果返回給上層程式。

Facade 具體應用

一個 Facade 模組可以針對一種需求,處理來自各方面的請求。例如在一個使用者註冊模組中,當前的需求描述為“只要使用者名稱沒有衝突,就允許註冊”。因為這是一種很寬鬆的註冊策略,所以我們不妨把相應的 Facade 類稱為 LenientFacade。

這個 Facade 類首先根據使用者輸入的資訊構造出一個 UserBean,然後檢測該使用者名稱是否已被佔用。如果未被佔用,就調用 DAO 執行增加記錄操作,否則就拋出一個“使用者名稱已存在”的異常。

 

 

public class LenientFacade {
    public int insertUser (ActionForm actionForm) throws ... {
        int row;
        UserForm userForm = (UserForm) actionForm;

        // 根據formBean產生UserBean
        UserBean userBean = new UserBean();
        BeanUtils.copyProperties(userBean, userForm);

        // 執行資料庫插入操作,首先查看該使用者是否存在。
        setUserDAO(new UserDAOibatis());
        UserBean user = dao.loadUserByUsername(userBean.getUsername());
        if (user != null) {
        // 同名使用者已存在,拋出一個自訂的異常 UserAlreadyExistException。
            throw new UserAlreadyExistException("User already exist.");
            }
        row = dao.insertUser(userBean);
        return row; // 返回該使用者的id(AUTO_INCREMENT)。
    }

    public void setUserDAO (UserDAO dao) {
        this.dao = dao;
    }

    private UserDAO dao;
}

 

 

在某一時期,可能因為各種政策的原因,網站需要對註冊使用者進行更為嚴格的審核。所以我們需要 Facade 具有不同的商務邏輯。不妨把負責這種更嚴格的註冊過程的 Facade 類稱為 StrictFacade。

 

StrictFacade 除了驗證使用者名稱是否可用外,還要驗證使用者的國籍、年齡、學曆等等一大堆事情。下面是虛擬碼:

 

 

public class StrictFacade {
    public int insertUser (ActionForm actionForm) throws ... {
       
        ...  //和前面相同的代碼

        // 執行資料庫插入操作,首先查看該使用者是否存在。
        setUserDAO(new UserDAOibatis());
        UserBean user = dao.loadUserByUsername(userBean.getUsername());
        if (user != null) {
        // 同名使用者已存在,拋出一個自訂的異常 UserAlreadyExistException。
            throw new UserAlreadyExistException("User already exist.");
            }

        try {
            // 許多許多更嚴格的審查
            // 許多許多更嚴格的審查

        }
        catch (... ) {
            ...
            throw 五花八門的異常           
        }


        row = dao.insertUser(userBean);
        return row; // 返回該使用者的id(AUTO_INCREMENT)。
    }

    public void setUserDAO (UserDAO dao) {
        this.dao = dao;
    }

    private UserDAO dao;
}

 

 

執行更加嚴格的註冊過程,並不意味著原有的“寬鬆式”註冊就沒有用了。網站可能需要隨時根據情況在兩種策略之間切換。顯然,每次切換註冊過程就停掉伺服器,重新編譯 Facade 層的代碼,然後再重啟伺服器,會讓老闆和所有的使用者瘋掉!

 

這時,使用靈活的抽象原廠模式就可以解決這個問題。

.

結合抽象原廠模式的 Facade 模式

抽象原廠模式可以根據初始參數的不同,生產出適應各種需求的具體實作類別。我們可以設計兩種工廠,一種是 LenientFacadeFactory,另一種是 StrictFacadeFactory,二者分別負責生產“寬鬆式”和“嚴格式”的 Facade。

 

這兩個工廠都繼承自他們的父類 —— 一個抽象的 FacadeFactory。這個抽象的 Factory 使用一個靜態方法返回具體的 Factory 類,並且為它的子類們定義了獲得Facade的get方法:public abstract Facade getFacade();,所有繼承了抽象工廠的具體工廠類,都要負責為其調用者返回一個實際可用的 Facade。

 

抽象的 Facade 工廠代碼如下:

 

 

public abstract class FacadeFactory {

    public static FacadeFactory getInstance(int facadeFactoryType) {
        switch (facadeFactoryType) {
            case LENIENT:
                return new LenientFacadeFactory();
            case STRICT:
                return new StrictFacadeFactory();
            default:
                return null;
        }
    }

    public abstract Facade getFacade();

    public static final int LENIENT = 0;
    public static final int STRICT = 1;
}

 

 

下面是 LenientFacadeFactory 的代碼,Strict 版本的與其類似。

 

 

public class LenientFacadeFactory extends FacadeFactory {

    public Facade getFacade() {
        return new LenientFacade();
    }
}
 

 

通過這段代碼,我們就可以得到本文一開始的那段代碼中的 LenientFacade 了。

 

在程式(在Strut中應該是一個Action)中的具體調用方法是:

 


int facadeType = FacadeFactory.LENIENT;

FacadeFactory factory = FacadeFactory.getInstance(facadeType);
Facade facade = factory.getFacade();

try {
    row = facade.insertUser(userForm);
}
catch (...) {
    ...
}
 

 

這樣做的好處很明顯:Servlet (或具體為一個Action)不需要去產生出一個具體的 Facade,所有的方法調用都是建立在統一的介面之上。當我們需要一個不同類型的 Facade 的時候,只需要調整上面代碼中第一行的 facadeType 變數,就能產生出相應的 Facade。因此實現了 Controller 層和 Model 層的松耦合

 

.

通過配置初始化參數實現“更松的”耦合

首先聲明!這並不是一個很好解決方案,這僅僅是為了複習鞏固所學的知識而自創的辦法。

 

上面的代碼中,問題沒有得到根本的解決,因為那個代表著 facade 類型的 facadeType 變數仍舊被寫入程式碼在程式中了。不過這個問題並不難解決,只要考慮到 ActionServlet 的本質是一個 Servlet,我們就可以通過為該 Servlet 設定一個初始化參數解決它。

 

可以在 web.xml 配置描述符中,為 ActionServlet 增加一個初始化參數:

 


<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>facadeType</param-name>
        <param-value>LENIENT</param-value>
    </init-param>

    <load-on-startup>0</load-on-startup>
</servlet>
 

然後,我們在程式中就可以讀取這個初始化參數,進而得到適用的 FacadeFactory 了。

 


 String facadeType = this.getServlet().getInitParameter("facadeType");
 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.