使用 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 類別