LoginSignup
2
1

More than 1 year has passed since last update.

コスト削減した Null Object パターン

Last updated at Posted at 2021-08-21

Null Object パターンの目的

オブジェクトを要求してその結果が null かどうかの判定が繰り返し現れる場合に、その null 値をNull Object に置き換える事で、判定処理を行っている手続き的コードを除外する。

if (is_null($user)) {
  $userName = '';
} else {
  $userName = $user->userName();
}

基本パターン

継承 ver.
スクリーンショット 2021-08-21 9.45.37.png

インターフェース ver.
スクリーンショット 2021-08-21 9.45.42.png

例(継承 ver.)

<?php
class User
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function isNull(): bool
    {
        return false;
    }

    public function userName(): string
    {
        return $this->name;
    }

    public static function nullObject(): NullUser
    {
        return new NullUser();
    }
}

class NullUser extends User
{
    public function __construct() {
        parent::__construct('');
    }

    public function isNull(): bool
    {
        return true;
    }
}

function displayUserName(User $user) {
  echo $user->userName();
}

displayUserName(new User('tanaka'));    // 'tanaka'
displayUserName(User::nullObject());  // ''

補足

クライアントが User と NullUser で同じ応答(今回だと userName の取得)を要求する場合は上記のようにできるが、異なる応答を要求する場合は依然として is_null 判定を行う事になる。

// my ページでユーザが取れない場合はエラー
if ($user->isNull()) {
  // エラー処理、エラー画面表示
}

注意点

Null Objectクラスをクラス毎に作成するのにコストがかかるため、得られる効果を考慮して導入するかどうかの判断が必要。

応用パターン

Null Object クラスを用意せず、Nullの場合の値をクラス内に定義する事で Null Objectパターンを実現する。
これによって、Null Object クラスの作成コストを減らすことができる。

<?php
class User
{
    private const NULL_VALUE = '';
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public static function nullObject(): self
    {
        return new self(self::NULL_VALUE);
    }

    public function equal(self $another): bool
    {
      reutrn $this == $another;
    }
}

$user = new User('tanaka');
$nullUser = User::nullObject();
$user->userName();      // 'tanaka'
$nullUser->userName();  // ''

if ($user->equal(User::nullObject())) { // nullObject の判定は equal() で行う
  // 特別処理
}

名前が空だけど存在するユーザを認めたい場合は Null Object と区別ができないためこのパターンは使えないが、実際には User 属性に Id などが入ることを考えると、全て空のユーザを Null Object と区別したいケースは想定しづらい。
もし応用パターンが不適な場合が出たら、基本パターンでやることを考える。

参考

新装版 リファクタリング―既存のコードを安全に改善する
アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1