接續前一篇文章說明 Manager 類別介紹,這篇來說明實戰的例子。
首先說明情境:
- 有個資源需要透過 HTTP 協定存取,而存取的方法不只一種,可以直接硬幹 HTTP Request,也可以使用 SDK。
- 這個資源可以做快取。
根據第一點,可以了解這是適合使用轉接器模式(Adapter Pattern)的情境;根據第二點,可以了解這適合使用代理模式(Proxy Pattern);而綜合這兩點,可以知道這是一個相同介面,不同實作的情境。
首先需要定義出業務需求上所要的主要互動行為。這個情境來說,主要就是取資料,因此可以建立一個介面如下:
interface Resource { public function get(); }
|
接著就是上述 1 和 2 的實作:
class Http implements Resource { public function __construct(private string $url) { }
public function get() { } }
class Sdk implements Resource { public function __construct(private ResourceSdk $sdk) { }
public function get() { } }
class Cached implements Resource { public function __construct(private Resource $real, private CacheInterface $cache) { }
public function get() { $config = $this->real->get(); return $config; } }
|
到這邊為止,已經寫了三個實作了。而我們會需要管理產生出來的實體,我們可以使用工廠方法模式來處理,當然也可以用今天的主角 Manager。
首先實作 Manager 的 Driver 建立實體的方法,這次的例子是三個實作,因此需要寫三個方法。先用硬幹的把它實作好:
class ResourceManager extends Manager { public function createHttpDriver() { 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), ) } }
|
這很硬幹,但不要緊,主要是要讓大家知道目前缺少的關鍵要素--就是設定。
依上面的需求,我們可以建立像下面的設定檔:
return [ 'default' => env('RESOURCE_DEFAULT_DRIVER', 'cached'), 'driver' => [ 'http' => [ 'url' => env('RESOURCE_HTTP_URL'), ],
'sdk' => [ ],
'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 類別。