ヌルオブジェクトパターンの概要

1. 概要

このクイックチュートリアルでは、https://www.baeldung.com/java-strategy-pattern [Strategy Pattern]の特殊なケースであるNull Object Patternを見ていきます。 その目的と、それを実際に使用することをいつ考慮すべきかを説明します。
いつものように、簡単なコード例も提供します。

2. ヌルオブジェクトパターン

ほとんどのオブジェクト指向プログラミング言語では、_null_参照を使用できません。 そのため、しばしば_null_チェックを書くことを余儀なくされます:
Command cmd = getCommand();
if (cmd != null) {
    cmd.execute();
}
場合によっては、そのような_if_ステートメントの数が多くなると、コードがくなり、読みにくくなり、エラーが発生しやすくなります。 これは、ヌルオブジェクトパターンが役立つ場合です。
  • Nullオブジェクトパターンの目的は、そのような_null_チェックを最小限にすることです。*代わりに、nullの動作を識別し、クライアントコードが期待する型にカプセル化できます。 多くの場合、そうではありませんが、そのような中立的なロジックは非常に単純です。何もしません。 この方法により、_null_参照の特別な処理を扱う必要がなくなります。

    nullオブジェクトは、実際にはより洗練されたビジネスロジックを実際に含む特定のタイプの他のインスタンスを処理するのと同じ方法で処理できます。 その結果、クライアントコードはクリーンなままです。
    nullオブジェクトには状態が含まれていないため、同一のインスタンスを複数回作成する必要はありません。 したがって、多くの場合、https://www.baeldung.com/java-singleton [* singletons *]として* nullオブジェクトを*実装*します。

3. Null Object PatternのUML図

パターンを視覚的に見てみましょう。
link:/uploads/NOP.jpg []
ご覧のとおり、次の参加者を特定できます。
  • _Client_には_AbstractObject_のインスタンスが必要です

  • _AbstractObject_は、_Client_が期待する契約を定義します。
    実装クラスの共有ロジックを含む

  • _RealObject_は_AbstractObject_を実装し、実際の動作を提供します

  • _NullObject_は_AbstractObject_を実装し、ニュートラルな動作を提供します

4. 実装

理論の明確なアイデアが得られたので、例を見てみましょう。
メッセージルーターアプリケーションがあるとします。 各メッセージには有効な優先度を割り当てる必要があります。 このシステムでは、優先度の高いメッセージをSMSゲートウェイにルーティングすることになっていますが、優先度が中程度のメッセージはJMSキューにルーティングする必要があります。
*ただし、「未定義」または空の優先度を持つメッセージがアプリケーションに届く場合があります。 このようなメッセージは、以降の処理から破棄する必要があります。
最初に、_Router_インターフェイスを作成します。
public interface Router {
    void route(Message msg);
}
次に、上記のインターフェイスの2つの実装を作成しましょう。SMSゲートウェイへのルーティングを担当する実装と、メッセージをJMSキューにルーティングする実装です。
public class SmsRouter implements Router {
    @Override
    public void route(Message msg) {
        // implementation details
    }
}
public class JmsRouter implements Router {
    @Override
    public void route(Message msg) {
        // implementation details
    }
}
最後に、* nullオブジェクトを実装しましょう:*
public class NullRouter implements Router {
    @Override
    public void route(Message msg) {
        // do nothing
    }
}
これで、すべてのピースをまとめる準備ができました。 サンプルのクライアントコードがどのように見えるかを見てみましょう。
public class RoutingHandler {
    public void handle(Iterable<Message> messages) {
        for (Message msg : messages) {
            Router router = RouterFactory.getRouterForMessage(msg);
            router.route(msg);
        }
    }
}
ご覧のとおり、どの実装が_RouterFactory._によって返されるかに関係なく、すべての_Router_オブジェクトを同じように処理します。

5. Nullオブジェクトパターンを使用する場合

*実行をスキップするか、デフォルトのアクションを実行するためだけに_Client_が_null_をチェックする場合は、Nullオブジェクトパターンを使用する必要があります。 _null_値。 このようにして、クライアントのコードは、特定のインスタンスが_null_であるかどうかを認識する必要がなくなりました。
このようなアプローチは、https://martinfowler.com/bliki/TellDontAsk.html [Tell-Don't-Ask]などの一般的なオブジェクト指向の原則に従います。
Null Object Patternを使用するタイミングをよりよく理解するために、次のように定義された_CustomerDao_インターフェイスを実装する必要があると考えてみましょう。
public interface CustomerDao {
    Collection<Customer> findByNameAndLastname(String name, String lastname);
    Customer getById(Long id);
}
ほとんどの開発者は、提供された検索条件に一致する顧客がない場合に、_findByNameAndLastname()_から_Collections.emptyList()_を*返します。 これは、ヌルオブジェクトパターンに従う非常に良い例です。
対照的に、_getById()_は指定されたIDを持つ顧客を返す必要があります。 このメソッドを呼び出す人は、特定の顧客エンティティを取得することを期待しています。 *そのような顧客が存在しない場合は、_null_を明示的に返して、指定されたIDに問題があることを通知する必要があります。 *
他のすべてのパターンと同様に、* Null Object Pattern *を盲目的に実装する前に、特定のユースケースを考慮する必要があります。 そうしないと、コードに見つけにくいバグが意図せずに導入される可能性があります。

6. 結論

この記事では、Null Object Patternとは何か、いつ使用するかを学びました。 また、設計パターンの簡単な例を実装しました。
いつものように、すべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns-behavioral[GitHub]で入手できます。