JavaのPrototypeパターン入門:オブジェクト複製のメリットと実装方法

JavaのPrototypeパターン入門:オブジェクト複製のメリットと実装方法

PR Amazonのアソシエイトとして、ITナレッジライフは適格販売により収入を得ています。

記事の文字数:2,786

JavaにおけるPrototype(プロトタイプ)パターンについて、基本概念から`Cloneable`インターフェースやコピーコンストラクタを使った実践的な実装例、導入するメリットまで分かりやすく解説します。

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

ベストセラー

世界一流エンジニアの思考法

難易度
実用性
思考法

技術スキル以上に大切な「考え方」をアップデートできる。世界基準の効率的な仕事術が学べます。

文系社会人のためのIT資格の歩き方: 文系のあなたに最適な道をご提案します (YKcreate kiki)

難易度
実用性
キャリア度

ロードマップが非常に明確。キャリアプランを見据えた、無駄のない学習順序を提案してくれます。

エンジニアを説明上手にする本 相手に応じた技術情報や知識の伝え方

難易度
実用性
コミュ力

「伝える技術」もエンジニアの重要なスキル。相手の視点に立った、具体的な説明のコツが満載です。

技術広報入門 ー テックブログから始めるエンジニアカルチャーのつくり方

難易度
実用性
独自性

テックブログを入口にエンジニア組織の文化をどう設計し、育てていくかという実践論が学べます。

「オブジェクトの生成処理が重くてパフォーマンスが低下している」「複雑な初期設定を持つインスタンスを使い回したい」といった悩みを抱えていませんか?本記事では、そのような課題を解決する「Prototypeprototype [プロトタイプ]動作確認や検証のために作成される試作品(プロトタイプ)パターン」について、JavaJava [ジャバ]オブジェクト指向プログラミング言語での具体的な実装例を交えてわかりやすく解説します。

記事のポイント

  • Prototypeパターンは、既存のインスタンスをコピー(複製)して新しいインスタンスを生成するデザインパターンです。
  • 生成コストが高いオブジェクトを毎回ゼロから構築するのを避け、初期化処理のコストを削減できる場合があります。
  • Javaでは Cloneable インターフェースと clone() メソッドを使う方法が有名ですが、近年はコピーコンストラクタを利用する安全な手法も推奨されます。
  • 複製時には「シャローコピー(浅いコピー)」と「ディープコピー(深いコピー)」の違いを理解することが重要です。

JavaにおけるPrototype(プロトタイプ)パターンとは?

Prototype(プロトタイプ) パターンは、GoF(Gang of Four)によって定義された23のデザインパターンのうち、「生成に関するパターン」の1つです。

通常のオブジェクト指向プログラミングでは、new キーワードを使用してクラスからインスタンスをゼロから生成します。しかし、Prototypeパターンでは、あらかじめ用意しておいた「原型(プロトタイプ)」となるインスタンスを**複製(コピー)**することで、新しいオブジェクトを作り出します。

PrototypeパターンをJavaで使うべき場面とメリット

  • インスタンス生成のコストが高い場合: データベースからのデータ取得や複雑な計算など、オブジェクトの初期化に時間がかかる場合、毎回ゼロから構築するより、初期化済みのオブジェクトをコピーすることで処理を効率化できる場合があります。
  • 実行時にクラスが決まる場合: クライアント側が具体的なクラス名を知らなくても、手元にあるインスタンス(インターフェース)をコピーするだけで同じ型のインスタンスを生成できます。
  • 似たような状態を持つオブジェクトを大量に作る場合: 初期化コストがコピー自体のコストを上回る場合、初期設定が済んだオブジェクトのコピーを作り、一部だけ変更する方が効率的です。

Java Prototypeパターンの実装例(Cloneableインターフェース)

Javaには、インスタンスを複製する標準的な仕組みとして Cloneable インターフェースと Object.clone() メソッドが用意されています。

まずは、この標準機能を使った実装例を見てみましょう。

1. Prototypeの定義と実装(cloneメソッド)

モンスター(Monster)を複製して大量にスポーンさせるゲームの仕組みを考えてみます。

// 複製可能であることを示すために Cloneable を実装
public class Monster implements Cloneable {
private String name;
private int hp;
public Monster(String name, int hp) {
// ここに重い処理(DBアクセスやアセットのロードなど)があると仮定
try {
Thread.sleep(1000); // 1秒かかる重い処理のシミュレート
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.hp = hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public void showStatus() {
System.out.println("Monster: " + name + ", HP: " + hp);
}
// cloneメソッドをオーバーライドして公開(publicにする)
@Override
public Monster clone() {
try {
// Objectクラスのclone()を呼び出して自身を複製
return (Monster) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}

2. クライアント側での呼び出しと実行例

new を使った場合と、clone() を使った場合の挙動の違いを見てみます。

public class Main {
public static void main(String[] args) {
System.out.println("--- 最初のモンスターを生成 (new) ---");
long start1 = System.currentTimeMillis();
Monster originalSlime = new Monster("スライム", 100);
long end1 = System.currentTimeMillis();
System.out.println("生成時間: " + (end1 - start1) + "ms");
originalSlime.showStatus();
System.out.println("\n--- モンスターを複製 (clone) ---");
long start2 = System.currentTimeMillis();
// 原型(プロトタイプ)から複製。重いコンストラクタは呼ばれない!
Monster clonedSlime1 = originalSlime.clone();
Monster clonedSlime2 = originalSlime.clone();
long end2 = System.currentTimeMillis();
System.out.println("複製時間: " + (end2 - start2) + "ms");
// 複製したモンスターの値を変更しても原型には影響しない
clonedSlime1.setHp(80);
clonedSlime2.setHp(50);
originalSlime.showStatus();
clonedSlime1.showStatus();
clonedSlime2.showStatus();
}
}

実行結果のイメージ: clone() による単純なコピーは、重い初期化処理を繰り返す必要がないため、今回の例では new より大幅に短時間で完了します。これは、Object.clone() がコンストラクタを呼び出さず、新しいインスタンスを作成してフィールドの値をコピーするためです。

※補足:上記の Monster クラスの例では、フィールドが String(不変オブジェクト)と int(基本データ型)のみであるため、シャローコピーによる問題は発生しません。しかし、リストなどの変更可能な参照型フィールドを持つ場合は注意が必要です。詳しくは次のセクションで解説します。

Javaでclone()を使う際の注意点:シャローコピーとディープコピー

Object.clone() メソッドを使用する際に必ず理解しておかなければならないのが、**シャローコピー(浅いコピー)**の問題です。

super.clone() は、フィールドの値をそのままコピーします。

  • 基本データ型(intbooleanなど)は値そのものがコピーされるので問題ありません。
  • しかし、参照型(配列やList、他のオブジェクトなど)の場合、参照(メモリアドレス)だけがコピーされてしまいます。

つまり、複製したオブジェクトが持つリストを変更すると、元のオブジェクトのリストも変更されてしまう(共有されている状態になる)というバグを引き起こしやすくなります。これを防ぐためには、開発者が手動で参照先のオブジェクトも新しく生成してコピーする**ディープコピー(深いコピー)**を実装する必要があります。

コピーコンストラクタを使ったモダンなJava Prototype実装

Java界隈(特にEffective Javaなどのベストプラクティス)では、Cloneable インターフェースには設計上の欠陥が多いと指摘されており、代わりにコピーコンストラクタコピーファクトリーを使用することが推奨されています。

public class Player {
private String name;
private List<String> items;
// 通常のコンストラクタ
public Player(String name, List<String> items) {
this.name = name;
this.items = new ArrayList<>(items);
}
// コピーコンストラクタ(自分自身と同じ型を引数に取る)
public Player(Player prototype) {
this.name = prototype.name;
// List自体を新しく作成する。
// Stringは不変オブジェクトなので、要素のコピーは不要。
this.items = new ArrayList<>(prototype.items);
}
// コピーファクトリーメソッドの場合
public static Player newInstance(Player prototype) {
return new Player(prototype);
}
}
// 利用側
Player original = new Player("勇者", Arrays.asList("剣", "盾"));
Player cloned = new Player(original); // コピーコンストラクタで複製

ここでは List 自体を新しく作成しています。リストの要素である String は不変オブジェクトなので、要素まで個別に再帰的コピーをする必要はありません。

コピーコンストラクタには以下のようなメリットがあります。

  • Cloneable のようなマーカーインターフェースや例外(CloneNotSupportedException)の処理が不要。
  • final フィールドとも相性が良い(オーバーライドした clone メソッド内で、ディープコピーのために final な参照型フィールドへ新しいオブジェクトを再代入しようとしてもコンパイルエラーになるという制約を受けないため)。
  • コピー用のコンストラクタでは、必要に応じて別の型やインターフェースから値を取り込むコンストラクタを用意することもできます。ただし、一般に「コピーコンストラクタ」と呼ばれるのは、同じクラスのインスタンスを引数に取る形式です。

まとめ:JavaのPrototypeパターンでオブジェクト生成を最適化

Prototypeパターンは、 「新しいインスタンスを作るために、既存のインスタンスをコピーする」 というシンプルなアイデアですが、オブジェクトの初期化コストが高い場合には、生成処理を効率化できる可能性があります。

  • オブジェクトの初期化処理(コンストラクタ)が非常に重い場合。
  • 複雑な状態を保ったままのオブジェクトの「スナップショット」を作りたい場合。

このような場面では、Prototypeパターンの導入を検討してみてください。 また、Javaで実装する際は、手軽な Cloneable / clone() だけでなく、安全で確実なコピーコンストラクタの手法もあわせて覚えておくと、バグの少ない堅牢な設計ができるようになります。


以上で本記事の解説を終わります。
よいITライフを!
ITナレッジライフ 運営者
この記事を書いた人

Z (ITナレッジライフ)

現役のITエンジニア。Linux、プログラミング、IT用語など、日々の業務で得た「痒いところに手が届く」技術情報を発信しています。

プロフィールと編集ポリシーを見る

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

人気記事


記事を評価

Thanks!
目次
Scroll to Top