設計模式系列·Facade模式之MVC的煩惱

子路發表於2017-03-28

前言

以小說的筆法寫的設計模式系列文章,你絕對看得懂![首發於公眾號:"聊聊程式碼"]

設計模式系列·王小二需求歷險記(一)
設計模式系列·王小二需求歷險記(二)
設計模式系列·封裝、繼承、多型
設計模式系列·初探設計模式之王小二的疑問
設計模式系列·Facade模式之MVC的煩惱
設計模式系列·Adapter 模式之如何優雅的使用別人的輪子
設計模式系列·類爆炸之Bridge模式
設計模式系列·工廠方法模式之 Code Review
設計模式系列·抽象工廠模式

------華麗的分割線------

流行的MVC架構模式

如今的Web開發,各種框架風起雲湧,勢如破竹。

從國民第一的ThinkPhp到稱霸全球的Laravel,這些框架有一個共同特徵,都採用了MVC的架構模式。

設計模式系列·Facade模式之MVC的煩惱

沒有任何意外,王小二的公司用Thinkphp來開發公司的主打產品。

Get新需求

一天,小二剛到公司,正打算坐下來喝杯茶。

老大走了過來:“小二啊,現在有個新的需求。我們們之前提交訂單的模組,需要增加傳送郵件的功能,你看看能不能實現?”

小二想了想說:“沒問題,最多3天搞定!”

看王小二胸有成竹的樣子,老大滿意的點了點頭。

臃腫的Controller

著手開幹吧!小二開啟熟悉的IDE,找到提交訂單模組的Controller。

OMG!不看不知道,一看嚇一跳,這個Controller的程式碼竟然接近2000行。

因為使用者提交訂單時,會與其他模組進行互動,需要的資料也比較複雜。
只見此Controller,從Model層各種拿資料,然後各種邏輯處理,怪不得程式碼到了將近2000行。

“哎,這2000行程式碼,看著就頭疼,可讓我怎麼寫啊”...小二嘆氣道。

“要不再去請教下C哥?”

MVC的煩惱

小二找到C哥,詳細的描述了他的問題。

C哥喝了口水,淡定的說:“這個嘛,我之前也遇到過。”

“您也遇到過,怎麼解決的?”

“這個問題,哈哈,姑且就叫MVC的煩惱吧!MVC將View與Model進行了分離解耦,這固然很好,但很多人就將業務邏輯的處理寫在了Controller裡,導致Controller越來越臃腫,以致最後都無法維護。”

“對對對,您說的太對了,我就經常這樣寫。”

[圖片:臃腫的程式碼]

設計模式系列·Facade模式之MVC的煩惱

給Controller減肥

C哥繼續說道:其實,Controller不應該處理過多的業務邏輯。給你舉兩個例子就明白了。

  1. 控制器,就像遙控器一樣。
    你見過遙控器關心電視怎麼播放視訊嗎?沒有,遙控器只是傳送播放視訊的訊號,具體的播放視訊的細節,遙控器不會關心。

  2. 控制器,就像將軍一樣。
    你見過將軍親自為每位士兵配備武器嗎?細節部分,將軍不必過問,將軍的職責是領兵打仗,這叫各司其職,否則就亂了。

說到這裡,小二恍然大悟:“聽C哥一席話,勝讀十年書啊!”

“既然這樣,就給Controller減減肥吧。”C哥說到
“是啊,但是怎麼減肥呢?”

初識Facade外觀模式

“我給你講一種設計模式-外觀模式,你就懂了”。
“好啊好啊,洗耳恭聽”。

C哥又講到:

外觀模式,提供了統一的介面,用來訪問子系統中的一群介面。外觀模式定義了一個高層介面,使得子系統更加易用。

也就是說,幹一件很複雜的事的時候,你想團隊中每個人都花一年半載去學習如何做這件事嗎?利用外觀模式,我只需要指定一個人去學會這些複雜的步驟,然後我再告訴這個介面人去幹就行了。

Facade外觀模式的應用

“如果讓你實現上面那個需求,你可能會找到使用者提交訂單的Controller,然後在Controller裡寫下面一大堆程式碼。是不是?”

/****檔名:SubmitController.class.php(使用者提交模組controller)****/

//..............接上...2000行程式碼..............//

    //獲取使用者郵箱
    public function get_user_email($uid){
        return new User()->get_user_email($uid);
    }
    //獲取要傳送給使用者的內容
    public function get_email_content($uid){
        return new Email()->get_email_content($uid);
    }
    //傳送郵件
    public function send_email($email,$content){
        return new Email()->send_email($email,$content);
    }

    //使用者提交訂單觸發的方法
    public function submit(){
        $email=$this->get_user_email($uid);
        $content=$this->get_email_content($uid);
        $this->send_email($email,$content);
    }複製程式碼

“對對對,我會這麼寫”。

"其實你用的ThinkPhp,有一層叫Logic層,關於業務邏輯處理的部分,你可以寫在Logic層裡。這樣,Controller層就變得很輕量了,好維護了。"

/****檔名:SendEmailFacadeLogic.class.php(傳送郵件Logic)****/

    //獲取使用者郵箱
    private function get_user_email($uid){
        return new User()->get_user_email($uid);
    }
    //獲取要傳送給使用者的內容
    private function get_email_content($uid){
        return new Email()->get_email_content($uid);
    }
    //傳送郵件
    public function send_email($uid){
        $email=$this->get_user_email($uid);
        $content=$this->get_email_content($uid);
        return new Email()->send_email($email,$content);
    }複製程式碼
/****檔名:SubmitController.class.php(使用者提交模組controller)****/

//..............接上...2000行程式碼..............//

    D('SendEmail','Logic')->send_email($uid);複製程式碼

“你看,加了Logic層,業務邏輯都放在Logic裡面去處理,Controller是不是瘦了很多呢?Logic層為Controller提供了一個高層的介面用來傳送郵件,也就是Facade模式的應用。”

加深理解

“小二,明白些了吧?”
“嗯嗯,明白了好多,猶如醍醐灌頂!”

“為了加深你的理解,我給你畫個簡單的例項圖吧”。
“真的嗎?太謝謝C哥了”。

設計模式系列·Facade模式之MVC的煩惱

恍然大悟

看了C哥畫的圖,小二小徹小悟了。

“C哥,Facade模式真不錯,你看,這樣統一成簡單的介面後:”

1、降低了系統的耦合度。提交訂單的Controller,再也不用與UserController、EmailController等耦合了。現在只需要關心SendEmailFacadeLogic就可以了。
2、並且,使用者使用了Facade模式後,有了統一的入口,就很容易監控客戶對系統的使用了。就如Thinkphp的單一入口一樣。

“嗯嗯。小二真聰明,確實是這樣。”

更多精彩,請關注公眾號“聊聊程式碼”,讓我們一起聊聊“左手程式碼右手詩”的事兒。

設計模式系列·Facade模式之MVC的煩惱

相關文章