RitoLabo

Interpreterパターン - PHPパターン

  • 公開:
  • カテゴリ: PHP DesignPatterns
  • タグ: PHP,Behavior,DesignPatterns,Composite,Visitor,Flyweight,Interpreter

Interpreterパターン(インタプリタ・パターン)は、振る舞いに関するデザインパターン手法の1つで、ある機能の「規則」を1つのクラスとして表現し振る舞いを定義する事で、機能拡張を容易にする処理モデルです。

Interpreterパターンの基本的なクラス図は以下の通りです。

Interpreterパターンクラス図

実装例

今回は、入力された1行のコマンドを解析し、実行していく流れをInterpreterパターンで実装します。

コマンドが実行されるには、先頭に「$」を、最後に「/」を入力する事を条件とし、それに適合していれば、入力されたコマンドに対応する処理を行います。

まずはContextクラスです。コマンドを保持し、要求に応じてコマンドを返します。

App/Context/Context.php
<?php

namespace App\Context;

class Context
{
private $commands;
private $index = 0;

public function __construct($command)
{
$this->commands = explode(' ', trim($command));
}

public function next()
{
$this->index++;
return $this;
}

public function getCommand()
{
if (!array_key_exists($this->index, $this->commands)) {
return null;
}
return $this->commands[$this->index];
}

}
  • コンストラクタで、受け取ったコマンドを解析し配列としてメンバ変数へ格納します。
  • getCommand() コマンドで、現在のポインタ(インデックス)にあるコマンドを返却します。
  • next() コマンドで、ポインタを次へ進めています。

次はExpressionクラスです。抽象クラスとしてインターフェイスを定義します。

App/Expression/Interfaces/ExpressionInterface.php
<?php

namespace App\Expression\Interfaces;

use App\Context\Context;

interface ExpressionInterface
{
public function execute(Context $context);
}

Expressionの具象クラスでは、execute()メソッドにてContextオブジェクトを受け取り処理を行うという流れになります。

最後にExpressionの具象クラス群です。

App/Expression/JobExpression.php
<?php

namespace App\Expression;

use App\Expression\Interfaces\ExpressionInterface;
use App\Context\Context;
use App\Expression\CommandExpression;

class JobExpression implements ExpressionInterface
{
public function execute(Context $context)
{
if ($context->getCommand() !== '$') {
throw new Exception('Missing opening tag "$"');
}
$command_list = new CommandExpression();
$command_list->execute($context->next());
}
}

JobExpressionクラスは、コマンドのルールである先頭の「$」があるかどうかを評価します。コマンドとしての条件に合致すれば、処理をCommandExpressionクラスへ委譲します。

App/Expression/CommandExpression.php
<?php

namespace App\Expression;

use App\Expression\Interfaces\ExpressionInterface;
use App\Context\Context;

class CommandExpression implements ExpressionInterface
{
public function execute(Context $context)
{
while (true) {
$text = $context->getCommand();
if (is_null($text)) {
throw new Exception('There is no closing command "/"');
} else if ($text === '/') {
break;
} else {
$expression = new DatetimeExpression();
$expression->execute($context);
}
$context->next();
}
}
}

CommandExpressionクラスでは、コマンドルールの最後に関しての評価を行います。併せてそれ以外の場合は、実行コマンドをみなし、DatetimeExpressionクラスへ処理を委譲します。

App/Expression/DatetimeExpression.php
<?php

namespace App\Expression;

use App\Expression\Interfaces\ExpressionInterface;
use App\Context\Context;

class DatetimeExpression implements ExpressionInterface
{
public function execute(Context $context)
{
$command = $context->getCommand();

switch ($command) {
case 'year':
echo date('Y') . ' ';
break;
case 'month':
echo date('m') . ' ';
break;
case 'day':
echo date('d') . ' ';
break;
case 'time':
echo date('H:i') . ' ';
break;
case 'second':
echo date('s') . ' ';
break;
}
}
}

DatetimeExpressionクラスでは、日時に関する処理を行います。渡されたコマンドに対して、必要な処理を行いますが、今回は簡単に日時を返すものになっています。

全ての実装が完了したのでクライアントから利用してみます。

index.php
<?php

use App\Context\Context;
use App\Expression\JobExpression;

// コマンド
$command = '$ year month day time second /';

$job = new JobExpression();
$job->execute(new Context($command));

結果は以下になります。

PHPでのInterpreterパターン実行結果

コマンドに基づいて処理が実行されたことを確認できました。

まとめ

Interpreter = 通訳」を意味する通り、ドメイン固有言語やファイルに収録されているものを解析し、それによって処理を行う流れを構築します。

特化言語による処理、つまりはある機能に特化した言語的解釈による機能を実装する事で高速に問題を処理できる場合があります。

構文木を構築し、解析・処理するInterpreterパターン。是非試してみてください。