通过facade(尤其是realtime facade)来使代码更优雅
本文來自pilishen.com----原文鏈接; 歡迎作客我們的php&Laravel學(xué)習(xí)群:109256050
該篇翻譯整理自laravel創(chuàng)始人Taylor的文章:Expressive Code & Real Time Facades,屬于《Laravel底層核心技術(shù)實(shí)戰(zhàn)揭秘》這一課程《laravel底層核心概念解析》這一章的擴(kuò)展閱讀。laravel 5.4引入了realtime facade的功能,也即任何一個class都可以隨時拿來當(dāng)facade用,只要在其namespace前面加上Facades前綴即可。當(dāng)然這個功能不可能隨處都用到,但是偶爾呢,用它可以實(shí)現(xiàn)更簡潔優(yōu)雅、易于測試的代碼方案。雖然下面的例子講的是laravel 5.4的realtime facade,但是呢,其實(shí)也完全可以用在之前的版本上,因?yàn)樗^的realtime facade,無非就是系統(tǒng)自動給你注冊成facade而已,鑒于這個功能又不可能到處用到,所以即使在老的版本里,如果你發(fā)現(xiàn)facade的這種代碼實(shí)現(xiàn)方式更有吸引力,那么自己手動注冊一個facade也完全可以的。
接下來的示例是關(guān)于Laravel Forge的,laravel Forge是laravel官方推出的laravel項(xiàng)目部署管理平臺。當(dāng)使用Forge的時候,你得在Forge后臺將你服務(wù)器提供商的賬號信息填上,然后呢交由Forge來具體管理。那么,這里假設(shè)呢我們有一個Model叫Provider,也就是對應(yīng)著不同的主機(jī)提供商,比如國外的DigitalOcean、國內(nèi)的阿里云等。
<?php use App; use Illuminate\Database\Eloquent\Model; class Provider extends Model {// }這里呢假設(shè)我們將所有處理外來API請求的class放在App\Services文件夾下,我們得對應(yīng)每一個主機(jī)供應(yīng)商都有一個“service”class,假設(shè)DigitalOcean這家供應(yīng)商的service class是這樣的:
<?php namespace App\Services; use App\Contracts\ServerProvider; class DigitalOcean implements ServerProvider {public function createServer($name, $size){//} }接下來呢,我們得能夠解析這個服務(wù)類,基于我們model里的type這一欄的信息,我們可以使用工廠(factory)模式來實(shí)現(xiàn):
<?php namespace App\Services; use InvalidArgumentException;class ServerProviderFactory {public function make($type){switch ($type) {case 'DigitalOcean':return new DigitalOcean;case 'Linode':return new Linode;default:throw new InvalidArgumentException;}} }然后呢,我們就可以在需要的地方調(diào)用這個工廠,來相應(yīng)地創(chuàng)建一個server 服務(wù),比如假設(shè)在controller里調(diào)用:
<?php namespace App\Http\Controllers; use App\Provider; use Illuminate\Http\Request; use App\Services\ServerProviderFactory;class ServerController extends Controller {protected $factory;public function __construct(ServerProviderFactory $factory){$this->factory = $factory;}public function store(Request $request, Provider $provider){$service = $this->factory->make($provider->type);$response = $service->createServer($request->name, $request->size);//} }但是呢,我覺得這樣還是有些繁瑣,我想要是這樣來用該多好呢?
<?php namespace App\Http\Controllers; use App\Provider; use Illuminate\Http\Request; class ServerController extends Controller {public function store(Request $request, Provider $provider){$repsonse = $provider->service()->createServer($request->name, $request->size);//} }我們只想簡單地調(diào)用Provider這個實(shí)例上的service方法,然后就能獲取到其背后對應(yīng)的供應(yīng)商,然后就能直接地createServer。這樣來寫呢,可能更像是我們?nèi)粘V凶钪苯拥乃伎歼^程,雖然可能背后具體怎么實(shí)現(xiàn)你還沒搞懂。那么怎么來實(shí)現(xiàn)呢?假設(shè)不借助facade,我們或許可以這樣:
<?php namespace App; use App\Services\ServerProviderFactory; use Illuminate\Database\Eloquent\Model;class Provider extends Model {public function service(){return (new ServerProviderFactory)->make($this->type);} }貌似可行。但是這樣呢,因?yàn)檫@個factory類是直接在service方法內(nèi)部實(shí)例化的,這是不好的,后期我們無法用它來mock測試。那么如果用realtime facade的方式會怎么樣呢?
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Facades\App\Services\ServerProviderFactory;class Provider extends Model {public function service(){return ServerProviderFactory::make($this->type);} }現(xiàn)在,不僅看起來更簡潔優(yōu)雅,而且也可以測試了,因?yàn)閒acade可以進(jìn)行mock,比如說這樣:
<?php namespace Tests\Feature; use Mockery; use App\Provider; use Tests\TestCase; use App\Contracts\ServerProvider; use Facades\App\Services\ServerProviderFactory; use Illuminate\Foundation\Testing\RefreshDatabase;class ExampleTest extends TestCase {public function testBasicTest(){$provider = factory(Provider::class)->create(['id' => 1,'type' => 'DigitalOcean',]);$service = Mockery::mock(ServerProvider::class);ServerProviderFactory::shouldReceive('make')->with('DigitalOcean')->andReturn($service);$service->shouldReceive('createServer')->once()->with('web', '2GB')->andReturn('server-id');$response = $this->json('POST', '/api/providers/1/server', ['name' => 'web','size' => '2GB',]);$response->assertStatus(201);} }你會發(fā)現(xiàn)real-time facade最有用的地方就是構(gòu)建簡潔、優(yōu)雅的object APIs,同時呢又不會影響到代碼的可測試性。希望這能給你的實(shí)際開發(fā)帶來一定啟發(fā)。
總結(jié)
以上是生活随笔為你收集整理的通过facade(尤其是realtime facade)来使代码更优雅的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解模板模式及实际应用
- 下一篇: 多线程訪问共享数据(1)