バリデーターのあるべき論

## はじめに

最近「これはこうあるべきだ」というべき論を考えることが多く、バリデーターについても改めて考える機会があったのでまとめることにした。

## べき論

バリデーターはシンプルに__「検証すべき要素がバリデーションに通るか通らないか（true or false）を返すべきだ」__
その際に__「もしfalseの場合には何がダメだったのかの失敗原因も返すべきだ」__
true/falseしか返してはいけないのにエラーメッセージも返さなければならないというのが「べき論」としてすでに矛盾している。
これをうまく回避する方法を以下のサンプルコードで示す。

## サンプルコード

今回は例として「0より大きい数字ならバリデーションtrue, 0以下ならfalse」とした。

### case1

```case1.php
<?php
function isValid($value){
    if($value > 0){
        return array(
            'result'  => true,
            'message' => '',
        );
    } else {
        return array(
            'result'  => false,
            'message' => 'バリデート失敗',
        );
    }
}

$test1 = 0;
$test2 = 1;

var_dump(isValid($test1));
var_dump(isValid($test2));
```

結果

```
array(2) {
  ["result"]=>
  bool(false)
  ["message"]=>
  string(21) "バリデート失敗"
}
array(2) {
  ["result"]=>
  bool(true)
  ["message"]=>
  string(0) ""
}
```

愚直に値2つを配列として返している。
これは「べき論」で言うとtrue, false以外も返しているところが反している。
また扱いも複雑になり配列の要素（今回の例だと`result`, `message`）にアクセスしなければならない。

### case2

```php:case2.php
<?php
function isValid($value, &$message){
    if($value > 0){
        $message = '';
        return true;
    } else {
        $message = 'バリデート失敗';
        return false;
    }
}

$test1 = 0;
$test2 = 1;

$message1 = '';
$message2 = '';

var_dump(isValid($test1, $message1));
var_dump($message1);
var_dump(isValid($test2, $message2));
var_dump($message2);
```

結果

```
bool(false)
string(21) "バリデート失敗"
bool(true)
string(0) ""
```

こちらは第二引数に参照渡しでメッセージ格納用の変数を予め渡しておき、その中にメッセージを入れておくという方法である。
「べき論」でいうと矛盾しておらずそこそこ有用そうに見える。
しかし検証すべき要素以外のものをバリデーターに渡さなければならず、かつ参照渡しなのでいつどこでメッセージの内容が書き換わるか分からずに副作用も強い。
例えば`isValid`関数を呼び出したあとに`message`を表示させるまでに間に`message`変数を上書きすることが可能である点や、
上記のように`message1`, `message2`のように変数を分けずに使いまわした場合に「どのタイミングでのメッセージなのか？」という状態が発生する。
有用だが十分に気を付けて使う必要がある。

### case3

```php:case3.php
<?php
class Validator
{
    private $message;

    public function __construct(){
        $this->message = '';
    }

    public function isValid($value){
        if($value > 0){
            $this->message = '';
            return true;
        } else {
            $this->message = 'バリデート失敗';
            return false;
        }
    }

    public function getMessage(){
        return $this->message;
    }
}

$test1 = 0;
$test2 = 1;
$validator = new Validator();

$result1  = $validator->isValid($test1);
$message1 = $validator->getMessage();
var_dump($result1, $message1);

$result2  = $validator->isValid($test2);
$message2 = $validator->getMessage();
var_dump($result2, $message2);
```

結果

```
bool(false)
string(21) "バリデート失敗"
bool(true)
string(0) ""
```

多分これが1番良いと思います（）
バリデーターを1つにクラスにしてしまえばプロパティにメッセージを持つことができるので、バリデーションと同時にメッセージを格納、欲しい時にgetする。
プロパティは`private`にしておけば上書きされる心配はない。
バリデーターのインスタンスを別途立てることで状態を持つことも回避する。

## おわりに

今回バリデーターの「あるべき論」について考え、ケースを3つ挙げました。
私の結論で言うとケース3が今のところ良いかなと思っていますが、これがベストかどうかは正直分かりません。
日々より良い方法を模索していきたいですね。

## おまけ

仕事で`php`を書く機会が多いので`php`で書きましたが、個人的には`ruby`が好きなので`ruby`で上記3パターンを書いたものも置いておきます。
何かコードを書く際に複数言語で書けるとかっこいいですね:smile: 

### case1

```rb:case1.rb
def isValid(value)
  if value > 0
    return {result: true, message: ''}
  else
    return {result: false, message: 'バリデート失敗'}
  end
end

test1 = 0
test2 = 1

p isValid(test1)
p isValid(test2)
```

結果

```
{:result=>false, :message=>"バリデート失敗"}
{:result=>true, :message=>""}
```

`php`の連想配列は`ruby`では`Hash`で表現した。

### case2

```rb:case2.rb
def isValid(arr)
  if arr[0] > 0
    arr[1] = ''
    return true
  else
    arr[1] = 'バリデート失敗'
    return false
  end
end

test1 = 0
test2 = 1

message1 = ''
message2 = ''

arr1 = [test1, message1]
arr2 = [test2, message2]

p isValid(arr1)
p arr1[1]
p isValid(arr2)
p arr2[1]
```

結果

```
false
"バリデート失敗"
true
""
```

`ruby`では参照渡しというものがないので上記ケース2を`ruby`で表現するのは難しかった。
配列を渡すことで擬似的に参照渡しを表現した。

### case3

```rb:case3.rb
class Validator
    def isValid(value)
      if value > 0
        @message = '';
        return true
      else
        @message = 'バリデート失敗';
        return false
      end
    end

    def getMessage
        @message
    end
end

test1 = 0
test2 = 1
validator = Validator.new

p validator.isValid(test1)
p validator.getMessage
p validator.isValid(test2)
p validator.getMessage
```

結果

```
false
"バリデート失敗"
true
""
```

`ruby`で書いてもやっぱりケース3が1番しっくりくる。
`ruby`のプロパティはデフォルトで`private`なのでより安全。