前幾天在網上看到一種設計方式,闡述了一種讓代碼看起來跟擴展性高的一種思維方式,作者透過實際例子描述如何從一開始的設計演化到最後的概念,這樣子的設計概念其實充滿在 Laravel 當中,我認為是相當值得學習的一個作法與思維,以下為實際演示和相關我理解的註解:

首先作者提出了一種原有的代碼設計思路,這邊可以看到示例代碼,用來處理相關 throwreport 的機制:

class Flaky
{
    protected $throw = true;

    public function reportFailures()
    {
        $this->throw = false;
    }

    public function throwFailures()
    {
        $this->throw = true;
    }

    protected function handle(Exception $e)
    {
        $this->throw ? throw $e : report($e);
    }
}

這邊是一個很基礎的設計,可以看到透過呼叫 method 來改變 throw property 來改變最後 handle 的動作。然而當有新的方法,例如 logFailures,就必須調整兩個部分:

  1. 新增 logFailures 方法
  2. 調整 handle 邏輯

這樣會導致會大幅度去調整原有邏輯跟方法,而沒有一個通用的方法,而且新增的邏輯是有機會跟原本方法不一樣,因此作者透過步驟去分析跟設計,而這樣子的作法也是 Laravel 代碼庫中的實踐方法:

class Flaky
{
    protected $handleFailure;

    public function __construct()
    {
        $this->throwFailures();
    }

    public function handleFailures($callback)
    {
        // 這邊透過 callback 的行為來增加代碼彈性,使其作用域在內部
        $this->handleFailure = $callback;
    }

    public function reportFailures()
    {
        $this->handleFailures(function($e) {
            report($e);
        });
    }

    public function throwFailures()
    {
        $this->handleFailures(function($e) {
            throw $e;
        });
    }

    // new method
    public function logFailures()
    {
        $this->handleFailures(function($e) {
            logger($e)
        });
    }

    protected function handle(Exception $e)
    {
        call_user_func($this->handleFailure, $e);
    }
}

透過 handle 去 call handleFailure,除了內部提供的 reportFailures, throwFailures, logFailures,也可以直接呼叫 handleFailure 進行自定義方法:

$hanlder = (new Flaky)->handleFailure(function($e) {
    logger($e);
    throw ResponseException("Something went wrong");
})

讓代碼去保持一定可開放性也可以針對需求做出基礎的方法,可以作為一種設計思路。