テンプレートメソッドパターンのつもりで、こんなクラスを作っておいて
class Foo
{
protected function doSomething()
{
// 複雑な処理
}
public function run()
{
// 略
$this->doSomething();
// 略
}
}
基本的にはdoSomething()でいいんだけど、
子クラスによってはちょっと変わった動作をさせたい。
ただしdoSomething()は複雑な処理で、今後も変更がありそうだから、
class FooChild
{
protected function doSomething()
{
// 前処理
parent::doSomething();
// 後処理
}
}
みたいに必ず Foo#doSomething() を呼ぶというコーディング規約で縛っとこう。
と思ったが、現実にはコーディング規約は無視され、
class FooChild2
{
protected function doSomething()
{
// Foo#doSomething()をコピペして改造した処理
}
}
となるはず。
んで Foo#doSomething() を変更したときFooChild2も変更しなきゃとなって死ぬ。
そこで考えた。
abstract class Bar
{
public function preSomething(Context $context) {}
public function postSomething(Context $context) {}
}
関数の形式として前処理、後処理として縛ってしまう。
class Foo
{
private function doSomething(Context $context)
{
// 略
if ($context->todoHoge) {
$this->doHoge();
}
// 略
}
public function run(Bar $bar, Context $context)
{
// 略
$bar->preSomething($context);
$this->doSomething($context);
$bar->postSomething($context);
// 略
}
}
必要があれば$contextの中にフラグを用意して、
Foo#doSomething()の中でif文なりで動作を切り替える。
ただし Foo#doSomething() の中をすっきりさせておくのは
コーディング規約に頼ることになってしまい、まだ心配の種は残る。
うーむ。
いくつかの処理を連続してやろうとすると、
PHPの文法の範疇ではコールバックの型を縛るのがしんどいなあ。
なんか変な気もするけど、洗練させていけば何かのデザインパターンに落ち着くのかな。
class Hoge1
{
public function execute()
{
$foo = new Foo();
$foo->askSomething(array($this, 'afterFoo'));
}
public function afterFoo(Response $res)
{
// $resに応じて何かの処理があって…
$bar = new Bar();
$bar->askSomething(array($this, 'afterBar'));
}
public function afterBar(Response $res)
{
// $resに応じて何かの処理があって…
$baz = new Baz();
$baz->askSomething(array($this, 'afterBaz'));
}
public function afterBaz(Response $res)
{
// $resに応じて何かの処理
}
}
class Hoge2
{
public function execute()
{
$foo = new Foo();
$res = $foo->askSomething();
// $resに応じて何かの処理があって…
$bar = new Bar();
$res = $bar->askSomething();
// $resに応じて何かの処理があって…
$buz = new Baz();
$res = $buz->askSomething();
// $resに応じて何かの処理
}
}
Buzz by 福冨諭 from Posted from the web
というわけで結果を
* 戻値で受け取る場合(execute1)
* コールバックで受け取る場合(execute2)
のコード片を書いてみた。
どっちも微妙っちゃあ微妙だなあ。
class Response
{
// 略
}
interface Responder
{
public function whenAccepted(Response $res);
public function whenRejected(Response $res);
}
class Hoge implements Responder
{
public function whenAccepted(Response $res)
{
// 依頼が受け付けられたとき
// $resに入っている受け付け番号とかの処理
}
public function whenRejected(Response $res)
{
// 依頼が拒否されたとき
// $resに入っている拒否理由とかの処理
}
public function execute1()
{
$foo = new Foo();
try {
$res = $foo->askSomething();
} catch (Exception $e) {
// エラー処理
}
if ($res->accepted) {
$this->whenAccepted($res);
} elseif ($res->rejected) {
$this->whenRejected($res);
}
// 共通の後片付け
}
public function execute2()
{
$foo = new Foo();
try {
$foo->askSomething($this);
} catch (Exception $e) {
// エラー処理
}
// 共通の後片付け
}
}