PHP
設計
enum

Enumを使ってフラグ値を良い感じに扱う

「間違った使い方ができない」ようなコードを書くためにはEnumが有用です。

元ネタ

https://speakerdeck.com/twada/php-conference-2016-revised

防御的プログラミング

コードを書く人間であれば誰しも,『良いコードとは何か』という問いに直面した経験があるかと思います。
これを一概に言い切ることは難しいですが,個人的にわかりやすい指標として「間違った使い方をする方が難しい」というのが挙げられると考えています。

コード例

元ネタ:https://twitter.com/yoookd/status/933711986071953409

具体的に、以下のコードを例にします。

<?php

class Something {
    public function praise($country) 
    {
        if($status === '1'){
            return 'Awesome';
        }else if ($status === '2'){
            return 'Excellent';
        }
        }else if ($status === '3'){
            return "...actually it's not bad";
        }
        throw new Exception('not defined country');
    }
}

引数にcountryを受取り,これが1の時はアメリカ人なのでAwesome,これが2の時はオーストラリア人なのでExcellent,これが3の時は英国人なので...actually it's not badと返します。
国籍設定に他意はありません。

この関数の「正しい」使い方を知るためには,関数の処理内容を見て「あ、1か2か3のどれかを渡すんだな」と理解する必要があります。あるいはコメントでそれを説明する方式もあります。いずれにせよ,関数の処理を文書かソースから理解し,関数を呼び出す側で1〜3以外の値が渡されないように工夫する必要があります。

Enumを使ったコード例

引用:https://qiita.com/Hiraku/items/71e385b56dcaa37629fe

<?php
abstract class Enum
{
    private $scalar;

    public function __construct($value)
    {
        $ref = new ReflectionObject($this);
        $consts = $ref->getConstants();
        if (! in_array($value, $consts, true)) {
            throw new InvalidArgumentException;
        }

        $this->scalar = $value;
    }

    final public static function __callStatic($label, $args)
    {
        $class = get_called_class();
        $const = constant("$class::$label");
        return new $class($const);
    }

    //元の値を取り出すメソッド。
    //メソッド名は好みのものに変更どうぞ
    final public function valueOf()
    {
        return $this->scalar;
    }

    final public function __toString()
    {
        return (string)$this->scalar;
    }
}

final class Country extends Enum
{
    const USA = '1';
    const AUSTRALIA = '2';
    const BRITISH = '3';
}

class Something {
    public function praise(Country $country) 
    {
        switch($country->value()){
            case Country::USA:
                return 'Awesome';
            case Country::AUSTRALIA:
                return 'Excellent';
            case Country::BRITISH:
                return "...actually it's not bad";
        }
    }
}

この書き方だと,例えばpraise関数に対して4という値を与えるとInvalidArgumentExceptionが発生します。先のコード例でも4という値を渡すと正しく動かないという点では共通していますが,InvalidArgumentExceptionというメッセージが出されることで,「あ、値の渡し方が間違えているんだな」と利用者に対して理解してもらうことができます。