1. パターンの意図
プロトタイプ(Prototype) パターンは、既存のオブジェクトをコピーして新しいインスタンスを生成する デザインパターンです。
解決する問題
- 複雑な初期化処理を毎回走らせたくない
-
new
を使ってゼロからオブジェクトを構築するのではなく、既存オブジェクトを土台にして効率的に複製したい - 生成時に「元の設定を保ちつつ、一部だけ変更したい」ケース
ポイント
- clone() / copy() でオブジェクトを複製
- 初期化コストの削減
- 元のオブジェクトをひな型(Prototype)として利用
2. UML 図
3. Flutter / Dart 実装例
3.1 プロトタイプのインターフェース
abstract class Prototype<T> {
T clone();
}
3.2 具体クラス
class User implements Prototype<User> {
final String name;
final int age;
final String? email;
User(this.name, this.age, {this.email});
@override
User clone() => User(name, age, email: email);
User copyWith({String? name, int? age, String? email}) {
return User(
name ?? this.name,
age ?? this.age,
email: email ?? this.email,
);
}
@override
String toString() => 'User(name: $name, age: $age, email: $email)';
}
3.3 利用例
void main() {
var user1 = User("Anna", 25, email: "anna@example.com");
var user2 = user1.clone();
var user3 = user1.copyWith(name: "Bob");
print(user1); // User(name: Anna, age: 25, email: anna@example.com)
print(user2); // User(name: Anna, age: 25, email: anna@example.com)
print(user3); // User(name: Bob, age: 25, email: anna@example.com)
}
4. Android / Kotlin 実装例
4.1 Prototype インターフェース
interface Prototype<T> {
fun clone(): T
}
4.2 具体クラス
data class User(
val name: String,
val age: Int,
val email: String? = null
) : Prototype<User> {
override fun clone(): User = copy()
}
4.3 利用例
fun main() {
val user1 = User("Anna", 25, "anna@example.com")
val user2 = user1.clone()
val user3 = user1.copy(name = "Bob")
println(user1) // User(name=Anna, age=25, email=anna@example.com)
println(user2) // User(name=Anna, age=25, email=anna@example.com)
println(user3) // User(name=Bob, age=25, email=anna@example.com)
}
💡 Kotlin の data class
はデフォルトで copy()
を備えているため、Prototype パターンの実装が非常に簡単。
5. メリット / デメリット
メリット
- 初期化コストを削減(新規生成より速い)
- 既存オブジェクトをテンプレートにできる
- 元のオブジェクトを変更せず、部分的に変更したコピーを作れる
デメリット
- 深いコピー vs 浅いコピー の問題(ネストされたオブジェクトをどう扱うか)
- 複製対象が複雑すぎると実装が大変
6. 実務ユースケース
Flutter
-
copyWith
パターン(TextStyle
,ThemeData
など Flutter 標準でも多用) - Widget の設定テンプレートを元に部分変更
- ネットワークレスポンスモデルの複製
Android (Kotlin)
-
data class.copy()
によるモデル複製 - ViewModel 内の状態管理(Immutable モデルの部分更新)
- 設定オブジェクトのテンプレート化
7. 実装上の注意点
Flutter / Dart
-
copyWith
を定義するのが事実上の標準 -
freezed
パッケージを使うと自動生成できる
Android / Kotlin
-
data class
のcopy()
が事実上の Prototype 実装 - ネストオブジェクトを含む場合は
deep copy
をどう扱うか設計要
8. どんなときに使う?
- 同じようなオブジェクトを多数生成したいとき
- 一部だけ変更した新インスタンスが欲しいとき
- 初期化コストが高いオブジェクトを効率よく複製したいとき
まとめ
- Prototype パターンは 既存オブジェクトの複製による効率的なインスタンス生成
- Flutter →
copyWith
、Android →data class.copy()
で日常的に利用されている - 浅いコピー / 深いコピー の扱いが設計上の注意点
- 「同じだけど少し違う」オブジェクトを簡単に作れる点が最大の強み