JavaにおけるNull Objectパターン: グレースフルなデフォルトによるエラー処理の効率化
別名
- アクティブな「何もしない」
- スタブ
Null Objectデザインパターンの意図
Null Objectパターンは、Javaアプリケーションを効率化し、nullチェックを実行せずに、不在のオブジェクトをシームレスに処理する不可欠なJavaデザインパターンです。
実世界の例を用いたNull Objectパターンの詳細な解説
実世界の例
Null Objectパターンの現実世界の例えは、カスタマーサービスの文脈で見つけることができます。さまざまなタイプのサポート担当者がいるカスタマーサービスシステムを想像してください。人間のエージェントと自動化されたボットです。顧客のリクエストが受信されると、システムはそれを人間のエージェントに割り当てるか、エージェントが利用できない場合は、自動化されたボットに割り当てることができます。人間のエージェントも自動化されたボットも利用できない場合、システムはリクエストを「Null担当者」に割り当てます。
Null担当者は、何も実行しないプレースホルダーですが、サポート担当者が不在であるためにシステムがクラッシュしたりエラーが発生したりしないようにします。「リクエストを処理中です」のようなデフォルトの応答を、実際の処理なしで提供し、それによってシステムを安定させ、コードベース全体でnullチェックを必要としないようにします。
平易な言葉で
Null Objectパターンは、「空」のオブジェクトをグレースフルに処理します。
Wikipediaによると
オブジェクト指向プログラミングでは、nullオブジェクトは参照値がないか、定義された中立(「null」)動作を持つオブジェクトです。nullオブジェクトデザインパターンは、そのようなオブジェクトの使用とその動作(または欠如)を記述します。
JavaにおけるNull Objectのプログラミング例
Null Objectパターンを実装することにより、Java開発者は、アプリケーションが「空」のオブジェクトをよりグレースフルに処理し、コードの安定性と可読性を向上させることができます。
ノードから二分木を構築しています。通常のノードと「空」のノードがあります。ツリーを通常にトラバースするとエラーが発生しないように、必要に応じてNull Objectパターンを使用します。
これがNode
インターフェースの定義です。
public interface Node {
String getName();
int getTreeSize();
Node getLeft();
Node getRight();
void walk();
}
Node
には2つの実装があります。通常のNodeImpl
実装と、空のノード用のNullNode
です。
@Slf4j
public class NodeImpl implements Node {
private final String name;
private final Node left;
private final Node right;
public NodeImpl(String name, Node left, Node right) {
this.name = name;
this.left = left;
this.right = right;
}
@Override
public int getTreeSize() {
return 1 + left.getTreeSize() + right.getTreeSize();
}
@Override
public Node getLeft() {
return left;
}
@Override
public Node getRight() {
return right;
}
@Override
public String getName() {
return name;
}
@Override
public void walk() {
LOGGER.info(name);
if (left.getTreeSize() > 0) {
left.walk();
}
if (right.getTreeSize() > 0) {
right.walk();
}
}
}
public final class NullNode implements Node {
private static final NullNode instance = new NullNode();
private NullNode() {
}
public static NullNode getInstance() {
return instance;
}
@Override
public int getTreeSize() {
return 0;
}
@Override
public Node getLeft() {
return null;
}
@Override
public Node getRight() {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public void walk() {
// Do nothing
}
}
その後、次のようにエラーなしで二分木を構築およびトラバースできます。
var root = new NodeImpl("1", new NodeImpl("11", new NodeImpl("111", NullNode.getInstance(), NullNode.getInstance()), NullNode.getInstance()),
new NodeImpl("12", NullNode.getInstance(), new NodeImpl("122", NullNode.getInstance(), NullNode.getInstance())));
root.walk();
プログラムの出力
1
11
111
12
122
JavaでNull Objectパターンを使用するべき状況
- nullオブジェクトの代わりにデフォルトの動作を提供する必要がある場合。
- nullチェックを排除することにより、クライアントコードを簡素化するため。
- デフォルトのアクションがnull参照の処理よりも望ましい場合。
JavaにおけるNull Objectパターンの実世界の応用例
- ロギングシステムで一般的に使用され、Null ObjectはNullPointerExceptionsを防ぐのに役立ち、信頼性の高いJavaソフトウェア開発に不可欠なパターンになっています。
- 空のコレクションをグレースフルに処理するためにNullIteratorを使用するコレクション。
- 何もしないコンポーネントを表すためにNullComponentを使用できるGUIシステム。
Null Objectパターンのメリットとトレードオフ
メリット
- nullチェックの必要性を排除し、NullPointerExceptionのリスクを減らします。
- クライアントコードを簡素化し、可読性を高めます。
- 共通インターフェースを通じてデフォルトの動作を処理することにより、ポリモーフィズムの使用を促進します。
トレードオフ
- クラスが追加され、システムの全体的な複雑さを増大させる可能性があります。
- デフォルトの動作は、明示的なnull処理によってキャッチされる可能性のある潜在的な問題をマスクする可能性があります。
関連するJavaデザインパターン
- Strategy: Null Objectは、戦略が何もしないという、Strategyパターンの特殊なケースと見なすことができます。
- State: 両方のパターンが異なる状態や動作を処理できる点で似ています。Null Objectは何もしない状態のようなものです。
- Factory: 実際のオブジェクトの代わりにNull Objectを提供するのにしばしば使用されます。