SEEDS Creator's Blog

読者です 読者をやめる 読者になる 読者になる

分岐処理をオブジェクトで表現してみる

みなさんどうも、WEBエンジニアの kinu です。 好奇心で Smalltalk を参考に普段使っている PHP で分岐処理を if 文を使わずにオブジェクト指向で実装してみました。

目標

下にあげたような単純な分岐を処理できるようにする。 [code] $value = 1; if ($value == 1) { echo 'True'; } else { echo 'False'; } [/code]

実装内容

処理の内容はシンプルで Logical にセットした値によって Bool の具象クラスが生成され、それがifTrue と ifFalse に渡されたクロージャを実行するかを決定するようになってます。

logical class figure

以下ソースコードです。

Bool.php

[code] interface Bool { public function ifTrue($closure);

    public function ifFalse($closure);

} [/code]

True.php

[code] namespace Bool;

class True { public function ifTrue($closure) { $closure(); }

    public function ifFalse($closure){}

} [/code]

False.php

[code] namespace Bool;

class False { public function ifTrue($closure){}

    public function ifFalse($closure)
    {
        return $closure();
    }

} [/code]

Logical.php

[code] class Logical { private $value;

    public function __construct($logic)
    {
        $this->value (bool)$logic;
    }

    public static function set($logic)
    {
        return new self($logic);
    }

    public function asBoolean()
    {
        return $this->value ? new \Bool\True : new \Bool\False;
    }

    public function ifTrue($closure)
    {
        $this->asBoolean()->ifTrue($closure);
        return $this;
    }

    public function ifFalse($closure)
    {
        $this->asBoolean()->ifFalse($closure);
        return $this;
    }

} [/code]

使用例

[code] $value = 1; Logical::set($value == 1) ->ifTrue(function () { echo 'True'; }) ->ifFalse(function () { echo 'False'; }); [/code]

ちなみに Smalltalk だと

[code] | aValue | aValue := 1. aValue = 1 ifTrue: [Transcript show: 'True'] ifFalse: [Transcript show: 'False'] [/code]

やっぱり比較するとクロージャfunction ()...のくだりが長い…。
しかも、外のスコープの変数を使おうとすると、

[code] $value = 1; Logical::set($value == 1) ->ifTrue(function () use ($value) { echo $value.' は 1 だよ。'; }) ->ifFalse(function () use ($value) { echo $value.' は 1 じゃないよ。'; }); [/code]

さらにネストでもしようものなら、

[code] $value = 1; Logical::set(isset($value)) ->ifTrue(function () use ($value) { Logical::set($value == 1) ->ifTrue(function () use ($value) { echo $value.' は 1 だよ。'; }) ->ifFalse(function () use ($value) { echo $value.' は 1 じゃないよ。'; }); }); [/code]

…実用的ではないですね。

まとめ

参考に Smalltalk の実装を見ていたときにポリモーフィズムなんかのオブジェクト指向の基礎を学べました。なにより Smalltalk 環境の使い方も少しだけ理解できたのでよかったです。まあ、もともとそのつもりでやってみたというのもありますが。

出来上がったものに関しては… PHP は if文がありますしね…。
でもクロージャの use がいらなくなったりしたら使ってみてもいいかな。あとはPHPで論理値がオブジェクトになっていることによるメリットなんか見つけていきたいです。