1. Home
  2. PHP
  3. DesignPatterns
  4. Interpreterパターン - PHPパターン

Interpreterパターン - PHPパターン

  • 公開日
  • カテゴリ:DesignPatterns
  • タグ:PHP,Behavior,DesignPatterns,Composite,Visitor,Flyweight,Interpreter
Interpreterパターン - PHPパターン

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

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));

結果は以下になります。

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

まとめ

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

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

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級