目錄

目錄

使用 Laravel Manager 類別 - 實戰篇

目錄

接續前一篇文章說明 Manager 類別介紹,這篇來說明實戰的例子。

首先說明情境:

  1. 有個資源需要透過 HTTP 協定存取,而存取的方法不只一種,可以直接硬幹 HTTP Request,也可以使用 SDK。
  2. 這個資源可以做快取。

根據第一點,可以了解這是適合使用轉接器模式(Adapter Pattern)的情境;根據第二點,可以了解這適合使用代理模式(Proxy Pattern);而綜合這兩點,可以知道這是一個相同介面,不同實作的情境。

首先需要定義出業務需求上所要的主要互動行為。這個情境來說,主要就是取資料,因此可以建立一個介面如下:

interface Resource
{
    public function get();
}

接著就是上述 1 和 2 的實作:

class Http implements Resource
{
    public function __construct(private string $url)
    {
    }

    public function get()
    {
        // 使用 HTTP Client 打 API 取資料
    }
}

class Sdk implements Resource
{
    public function __construct(private ResourceSdk $sdk)
    {
    }

    public function get()
    {
        // 使用 SDK 打 API 取資料
    }
}

class Cached implements Resource
{
    public function __construct(private Resource $real, private CacheInterface $cache)
    {
    }

    public function get()
    {
        // 取 Cache
        $config = $this->real->get();
        // 存 Cache
        
        return $config;
    }
}

到這邊為止,已經寫了三個實作了。而我們會需要管理產生出來的實體,我們可以使用工廠方法模式來處理,當然也可以用今天的主角 Manager。

首先實作 Manager 的 Driver 建立實體的方法,這次的例子是三個實作,因此需要寫三個方法。先用硬幹的把它實作好:

class ResourceManager extends Manager
{
    public function createHttpDriver()
    {
        // $url = 'somewhere'
        return new Http($url);
    }

    public function createSdkDriver()
    {
        return new Sdk(
            $this->container->make(ResourceSdk::class),
        );
    }

    public function createCachedDriver()
    {
        return new Cached(
            $this->container->make(Resource::class),
            $this->container->make(CacheInterface::class),
        )
    }
}

這很硬幹,但不要緊,主要是要讓大家知道目前缺少的關鍵要素--就是設定。

依上面的需求,我們可以建立像下面的設定檔:

// config/resource.php
return [
    'default' => env('RESOURCE_DEFAULT_DRIVER', 'cached'),
    'driver' => [
        'http' => [
            'url' => env('RESOURCE_HTTP_URL'),
        ],

        'sdk' => [
            // Something about SDK config
        ],

        'cached' => [
            'real' => env('RESOURCE_DEFAULT_DRIVER', 'http'),
        ],
    ],
];

接著設定對應回 Manager 的程式會長這樣:

class ResourceManager extends Manager
{
    public function getDefaultDriver()
    {
        return $this->config['resource.default'];
    }

    public function createHttpDriver()
    {
        return new Http(return $this->config['resource.http.url']);
    }

    public function createSdkDriver()
    {
        return new Sdk(
            $this->container->make(ResourceSdk::class),
        );
    }

    public function createCachedDriver()
    {
        return new Cached(
            $this->driver($this->config['resource.http.url']),
            $this->container->make(CacheInterface::class),
        )
    }
}

因為 Manager 也可以拿來當 Resource 用,因此也可以把 Interface 加上去:

class ResourceManager extends Manager implements Resource
{
    public function get()
    {
        return $this->driver()->get();
    }
}

最後的實作是 Service Provider 的綁定:

$this->app->singleton(Resource::class, ResourceManager::class);

注入的時候會拿到的是 ResourceManager,但實際上可以把它當 Resource 用,而且 Manager 會幫忙管理使用哪個實體,只要調整設定檔就能夠正確的使用對應的實體,整體來說是非常方便的。

這篇文章主要是以情境為例實作程式,而原理可以再參考前一篇文章使用 Laravel Manager 類別