
BuilderやFactoryが「新しく作る」設計なのに対して、Prototypeは
「すでにあるものをコピーして作る」 設計パターンです。
概要
このデザインパターンは、オブジェクトを 複製(コピー)して新しいインスタンスを作るパターンです。
最初にひな型を作っておき、そこから複製して使うようにします。
イメージできる例としては、
ケーキ屋さんで、「このデザインのケーキをもう1個!」と頼んだら、新しく作るのではなく“元の型をコピーして作る”ようなもの。
サンプルコード
Prototypeクラス
class Robot {
constructor(name, model) {
this.name = name;
this.model = model;
}
sayHello() {
console.log(`こんにちは!私は${this.name}型ロボットです。`);
}
clone() {
// 自分自身を複製して返す
return new Robot(this.name, this.model);
}
}
利用例
const original = new Robot("A1", "T-900");
original.sayHello(); // → こんにちは!私はA1型ロボットです。
// プロトタイプをもとに複製
const copy = original.clone();
copy.name = "B2";
copy.sayHello(); // → こんにちは!私はB2型ロボットです。
original.sayHello(); // → こんにちは!私はA1型ロボットです。
JavaScript特有の「prototype」との違い
JSにはもともと「プロトタイプ継承(prototype chain)」がありますが、このデザインパターンのPrototypeとは別概念なので間違えないようにしましょう。
デザインパターンの方は、「オブジェクトをコピーして再利用する」仕組みのこと。
ポイント解説
clone() メソッドが重要。
新しいオブジェクトを生成する代わりに、既存のオブジェクトをコピーする。
この場合は、シャローコピーではなく、ディープコピーをするのが鉄則です。
(参照にしてしまうと、prototypeではなくなります)
元の設定や構造を保ったまま、少しだけ変更した新インスタンスを作れる。
メリット
・生成コストが高いオブジェクトを効率的に再利用できる
・クラス階層を増やさずに新しいインスタンスを作れる
・実行時に新しいオブジェクトを柔軟に生成できる
デメリット
・深いネスト(入れ子構造)だと「浅いコピー/深いコピー」の扱いに注意が必要
・コピー処理を正しく実装しないと、参照共有のバグが起きやすい
コピーするデータの階層が深い場合は、以下のようにディープコピーを意識して処理する必要があります。
class Prototype {
clone() {
return structuredClone(this); // ES2021以降
}
}
const data = new Prototype();
data.config = { theme: "dark", lang: "ja" };
const copy = data.clone();
copy.config.theme = "light";
console.log(data.config.theme); // → "dark"(コピーなので影響なし)
よく使われるシーン
・大きな設定オブジェクトを複製して使う
・ゲームで「敵キャラのテンプレート」から複数生成する
・GUI要素をコピーして別の画面に展開
・既存データをテンプレート化して編集
あとがき
Javascriptは言語の中で元々Prototypeという機能があるので、混乱する人もいるかもしれませんが、
デザインパターンのPrototypeとは別機能とちゃんと分別するようにしないといけませんね。
でも、そもそもJSのPrototypeもこのパターン概念から作られていると思うので、ニワトリとタマゴの匂いがプンプンします。(笑って聞き逃してください)
無駄にデータを複製するのではなく、ディープコピーを意識するのが、このデザインパターンの特徴ということがよくわかりました。
0 件のコメント:
コメントを投稿