Posted at

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

More than 1 year has passed since last update.

「間違った使い方ができない」ようなコードを書くためには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というメッセージが出されることで,「あ、値の渡し方が間違えているんだな」と利用者に対して理解してもらうことができます。