ReadDoubleのサービスコンテナの章に、結合の拡張と書いてあるのが目に入り、実装を拡張するのかインターフェースをデコレートできるのかどっちだろうと気になったので調べてみたら両方反応しました。色々使えそう。
コード
Foo.php
interface Foo
{
/**
* 関数の説明
*/
public function getString(): string;
}
FooImpl.php
/**
* クラスの説明
*/
class FooImpl implements Foo
{
private $bar = 'default';
/**
* 関数の説明
*/
public function setString(string $new): void
{
$this->bar = $new;
}
/** {@inheritdoc} */
public function getString(): string
{
return $this->bar;
}
}
FooDecorator.php
/**
* クラスの説明
*/
class FooDecorator implements Foo
{
public function __construct(Foo $base)
{
$this->base = $base;
}
/** @var Foo */
private $base;
/** {@inheritdoc} */
public function getString(): string
{
return 'decorated ' . $this->base->getString();
}
}
AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->extend(Foo::class, function (Foo $service, $app) {
return new FooDecorator($service);
});
$this->app->extend(FooImpl::class, function (FooImpl $service, $app) {
$service->setString('and changed');
return $service;
});
}
public $bindings = [
Foo::class => FooImpl::class
];
}
FooController.php
class FooController extends Controller
{
public function __invoke(Foo $foo): array
{
return ['message' => $foo->getString()];
}
}
FooControllerTest.php
class FooControllerTest extends \Tests\TestCase
{
use MockeryPHPUnitIntegration;
/**
* @test
*/
public function fooTest(): void
{
$this->withoutExceptionHandling();
$response = $this->get('/foo');
$response->assertJson(['message' => 'decorated and changed']);
}
}
実行結果
$ php artisan test --filter FooControllerTest
PASS Tests\Feature\App\Http\Controllers\FooControllerTest
✓ foo test
Tests: 1 passed
Time: 6.73s