1. 程式人生 > >如何更優雅的給控制器 “減負”

如何更優雅的給控制器 “減負”

文章 mode strong www AR available 一點 source 查看

MVC是一個非常偉大的概念,但是最近我發現一個現象,包括我自己,我們在最開始接觸MVC概念時,我們非常嚴謹地貫徹這種分層思想,Controller層處理業務邏輯,而Model層只是單純的處理數據I/O。但是,伴隨著我們項目體量的逐漸增大,控制器的負擔也越來越大。這樣一來會有一個非常明顯的弊端,當我們在定位BUG時,我們總是需要對照著代碼查看許久。除此之外,彼此的業務代碼並沒有太好的關聯,這使得我們想要抽出一個Service時就顯得極為困難。
因此,是時候給我們的控制器做一些“減負”了。這裏的減負並不意味著會違背MVC的設計思想,而是把我們的控制器層的業務適當的分給其他部分。
有使用過一些主流框架的朋友應該都知道,其實很多框架都給Controller

層做了一些“減負”的工作,比如KOA裏面的middleware,抑或是Laravel裏面的Event,Policy等。
但是事與願違,即使這些框架提供了這些幫助,但是許多人在實際項目中使用到的卻很少,當然,也有可能是我接觸到的代碼不夠多。究其原因,竊以為尚未意識到這種理念的重要性。因此,我在這裏總結了自己這些年來“減負”的一些經驗,同時我也會配合一些代碼予以解釋。當然,我所寫的未必全對,因此希望有幸看到的讀者能保持自己的獨立性。

Model分流

我們在寫代碼時往往會有這樣一種場景,我們需要對從Model取出來的數據進行加工,但是,加工數據的部分我們經常會放到控制器,畢竟這屬於業務邏輯,確實無可厚非,如下偽代碼所示:

// controller
public function userList()
{
    $users = array_map(function ($user){
//        這裏會對我們的代碼進行業務邏輯的加工
        $user[‘created_at‘] = date(‘Y-m-d‘ , $user[‘created_at‘]);
        // ...
        return $user;
    } ,$model->availableList());
}

// model
public function availableList()
{
    // 從數據庫取數據  
return $users; }

但是我們有沒有考慮過這樣一個問題,當我們同事來接手我們項目或者我們debug時,我們需要了解的代碼量非常大,特別是涉及到一些數據加工的格式問題,我們並不需要關心。或者換個角度,當我們遇到數據加工的bug時,我們能第一聯想到這段代碼是放在Model層時,是不是更加快捷呢?

// controller
public function userList()
{
    $users = $model->availableList();
//    處理其他邏輯
}

// model
public function availableList()
{
    // 從數據庫取數據 $users
    return array_map(function ($user){
//        這裏會對我們的代碼進行業務邏輯的加工
        $user[‘created_at‘] = date(‘Y-m-d‘ , $user[‘created_at‘]);
        // ...
        return $user;
    } , $users);
}

如上代碼所示,在Model層中已經幫我們封裝好了我們所需要的數據以及其格式,當我們在瀏覽他人代碼時,我們並不需要關心他的格式是怎麽加工的,我們只需要根據他對方法的命名就能知道是獲取的怎樣的數據。

分離Controller

在寫具體的方法之前,我想要闡述的一點是,我們在寫代碼的時候需要保持一定的前瞻性。什麽意思?雖然我們的大部分工作都是跟具體的業務邏輯打交道,但是我們經常會發現總會有重復的工作,那麽有的人會直接把這段代碼復制。但是,在我們復制之前,我們是不是可以問自己這樣一個問題:如果接下來還有類似的業務,我們還是復制嗎?我們是不是可以把這段基於我們項目的代碼抽象出一個Service呢?
我舉個例子,比如一個網站,可能會有打賞功能,可能也有付費閱讀功能,我們不難發現,這兩種付費有著相似的地方,比如創建本平臺訂單系統的業務邏輯,再比如回掉時可能存在的相同業務邏輯,所以這段代碼我們是不是可以以一個trait的形式做一個Service

trait PayService
{
    private $_callback = null;

    public function createOrder()
    {
//        處理你的業務邏輯,配置調用三方支付接口的參數等
    }

    public function callback()
    {
//        處理共同的回調邏輯
        
        $this->handler();
    }
}

這裏我們保留了一個handler方法來處理每個功能獨有的業務邏輯,至此,我們就可以非常方便的擴展我們的支付服務了。

給控制器減負的方法還有很多,比如對我們加工數據的部分,其實我們也可以不放到Model,我們也可以單獨開辟一層來處理我們的數據加工。讓控制器變得清晰明朗,每個人閱讀代碼時都能非常快速的了解控制器下的每個方法在處理什麽業務邏輯。這便是我們給控制器減負的目的。
我很喜歡「包」的概念和設計思想,當我們在使用包時,不僅僅意味著方便,更重要的是,他做為一個獨立的“組件”存在於我們的代碼邏輯中,與我們項目的代碼不存在任何的耦合,同時我們也無需知道他的具體實現。

文章首發地址:我的博客

如何更優雅的給控制器 “減負”