Javaにおけるプロトタイプパターン:効率的なインスタンス化のためのオブジェクトクローンをマスターする
別名
- クローン
プロトタイプデザインパターンの意図
プロトタイプパターンは、プロトタイプインスタンスを使用して作成するオブジェクトの種類を指定し、オブジェクトクローニングを通じて新しいインスタンスを作成するために使用されます。
プロトタイプパターンの詳細な説明と実際の例
実際の例
カスタムデザインの家具を製造する会社を想像してください。注文があるたびに最初から各家具を作成する代わりに、最も人気のあるデザインのプロトタイプを保持します。顧客が特定のデザインを注文した場合、会社はそのデザインのプロトタイプをクローンし、必要なカスタマイズを行うだけです。このアプローチにより、基本的な構造とデザインの詳細がすでに存在するため、時間とリソースを節約でき、会社は一貫した品質で注文を迅速に処理できます。
このシナリオでは、家具のプロトタイプはソフトウェアのオブジェクトプロトタイプのように機能し、既存のモデルに基づいて新しいカスタマイズされた家具を効率的に作成できます。
平易な言葉で言うと
既存のオブジェクトに基づいて、クローニングを通じてオブジェクトを作成します。
Wikipediaによると
プロトタイプパターンは、ソフトウェア開発における生成デザインパターンです。作成するオブジェクトの種類が、新しいオブジェクトを生成するためにクローンされるプロトタイプインスタンスによって決定される場合に使用されます。
Javaにおけるプロトタイプパターンのプログラム例
Javaでは、プロトタイプパターンは次のように実装することが推奨されています。まず、オブジェクトをクローニングするためのメソッドを持つインターフェースを作成します。この例では、Prototype
インターフェースがcopy
メソッドでこれを実現しています。
public abstract class Prototype<T> implements Cloneable {
@SneakyThrows
public T copy() {
return (T) super.clone();
}
}
この例には、異なるクリーチャーの階層が含まれています。たとえば、Beast
クラスとOrcBeast
クラスを見てみましょう。
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Beast extends Prototype<Beast> {
public Beast(Beast source) {}
}
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
public class OrcBeast extends Beast {
private final String weapon;
public OrcBeast(OrcBeast orcBeast) {
super(orcBeast);
this.weapon = orcBeast.weapon;
}
@Override
public String toString() {
return "Orcish wolf attacks with " + weapon;
}
}
あまり詳細には立ち入りたくありませんが、完全な例には、基本クラスのMage
とWarlord
も含まれており、エルフとオークの特殊な実装もあります。
プロトタイプパターンを最大限に活用するために、HeroFactory
クラスとHeroFactoryImpl
クラスを作成して、プロトタイプからさまざまな種類のクリーチャーを生成します。
public interface HeroFactory {
Mage createMage();
Warlord createWarlord();
Beast createBeast();
}
@RequiredArgsConstructor
public class HeroFactoryImpl implements HeroFactory {
private final Mage mage;
private final Warlord warlord;
private final Beast beast;
public Mage createMage() {
return mage.copy();
}
public Warlord createWarlord() {
return warlord.copy();
}
public Beast createBeast() {
return beast.copy();
}
}
これで、既存のインスタンスをクローンして新しいクリーチャーを生成することで、完全なプロトタイプパターンを実際に示すことができます。
public static void main(String[] args) {
var factory = new HeroFactoryImpl(
new ElfMage("cooking"),
new ElfWarlord("cleaning"),
new ElfBeast("protecting")
);
var mage = factory.createMage();
var warlord = factory.createWarlord();
var beast = factory.createBeast();
LOGGER.info(mage.toString());
LOGGER.info(warlord.toString());
LOGGER.info(beast.toString());
factory = new HeroFactoryImpl(
new OrcMage("axe"),
new OrcWarlord("sword"),
new OrcBeast("laser")
);
mage = factory.createMage();
warlord = factory.createWarlord();
beast = factory.createBeast();
LOGGER.info(mage.toString());
LOGGER.info(warlord.toString());
LOGGER.info(beast.toString());
}
これは、例を実行したときのコンソール出力です。
08:36:19.012 [main] INFO com.iluwatar.prototype.App -- Elven mage helps in cooking
08:36:19.013 [main] INFO com.iluwatar.prototype.App -- Elven warlord helps in cleaning
08:36:19.014 [main] INFO com.iluwatar.prototype.App -- Elven eagle helps in protecting
08:36:19.014 [main] INFO com.iluwatar.prototype.App -- Orcish mage attacks with axe
08:36:19.014 [main] INFO com.iluwatar.prototype.App -- Orcish warlord attacks with sword
08:36:19.014 [main] INFO com.iluwatar.prototype.App -- Orcish wolf attacks with laser
プロトタイプパターンの詳細な説明と実際の例

Javaでプロトタイプパターンを使用するタイミング
- インスタンス化するクラスが実行時に、たとえば動的ロードによって指定される場合。
- 製品のクラス階層と並行するファクトリーのクラス階層の構築を避けるため。
- クラスのインスタンスが、いくつかの異なる状態の組み合わせの1つしか持てない場合。適切な状態を毎回手動でインスタンス化するよりも、対応する数のプロトタイプをインストールしてクローンする方が便利な場合があります。
- オブジェクトの作成がクローンと比較してコストがかかる場合。
- インスタンス化する具体的なクラスが、実行時まで不明な場合。
Javaにおけるプロトタイプパターンの実際の応用
- Javaでは、
Object.clone()
メソッドはプロトタイプパターンの古典的な実装です。 - GUIライブラリは、ボタン、ウィンドウ、その他のウィジェットの作成にプロトタイプを使用することがよくあります。
- ゲーム開発では、同様の属性を持つ複数のオブジェクト(敵キャラクターなど)を作成します。
プロトタイプパターンの利点とトレードオフ
利点
Javaアプリケーションでプロトタイプパターンを活用する
- 新しいオブジェクトのインスタンス化の複雑さを隠します。
- クラスの数を減らします。
- 実行時にオブジェクトを追加および削除できます。
トレードオフ
- 複雑になる可能性のあるクローニングメカニズムの実装が必要です。
- 特にクラスに循環参照を持つ複雑なオブジェクトグラフがある場合、ディープクローンを正しく実装することは困難な場合があります。
関連するJavaデザインパターン
- 抽象ファクトリー:どちらもオブジェクトの作成に関与しますが、プロトタイプはプロトタイプインスタンスのクローンを使用し、抽象ファクトリーはファクトリーメソッドを使用してオブジェクトを作成します。
- シングルトン:シングルトンは、単一インスタンスのクローンを許可する場合、インスタンスを作成するためにプロトタイプを使用できます。
- コンポジット:プロトタイプは、コンポーネントツリーの動的な作成を可能にするために、コンポジット内でよく使用されます。