Prompt

ROROパターンがあれば、Builderパターンは不要じゃない?

その視点は、現代的な TypeScriptJavaScript の開発スタイルにおいては非常に鋭いし、ある意味で「正解」に近いよ!

ROROパターン(Receive an Object, Return an Object) は、名前付き引数(オブジェクトの分割代入)を活用することで、多くの「Builderパターンが解決したかった問題」を、より軽量かつ宣言的に解決してしまうからね。

でも、情報工学的な設計の文脈で見ると、両者には 「型安全性の厳格さ」「構築プロセスの複雑さ」 という点で、まだ使い分けの余地があるんだ。


1. ROROパターンが Builder を「不要」にした部分

かつて Java などの言語で Builder パターンが必要だった最大の理由は、「引数が多すぎると、どれが何の引数か分からなくなる(順序の罠)」 を防ぐためだったんだ。

  • ROROの勝利: TypeScript なら { name, age, gender }: UserParams と書くだけで、呼び出し側は name: などのキーを指定せざるを得ない。これで「引数の順序間違い」は完全に消滅する。

  • デフォルト値: 分割代入のデフォルト値指定 { name = 'Guest' } を使えば、Builder の .withDefaultName() みたいなメソッドも不要になる。


2. それでも Builder パターンが生き残る「3つのケース」

それでも、以下のようなシチュエーションでは、RORO よりも Builder のほうが「設計の意図」を表現しやすい場合があるよ。

① 構築に「順序」や「依存」がある場合

「まず A を設定しないと、B の設定メソッドを呼び出せないようにしたい」という 状態遷移を伴う構築 の場合、Builder ならメソッドの戻り値の型を変えることで、不正な構築をコンパイルレベルで防げる。

  • RORO: すべてのオプションが最初から目の前に並んでいるので、「この組み合わせはダメ」という制約をかけるのが難しい。

② イミュータブル(不変)なオブジェクトを段階的に作りたい場合

複雑な設定を、複数の関数や条件分岐を跨いで少しずつ組み立てて、最後に build() でガチッと固めたいときは Builder が便利だね。

  • RORO: 基本的に「一度に全部渡す」スタイルだから、段階的な構築には向かないんだ。

③ 表現力(ドメイン特化言語:DSL)

QueryBuilder のように、.where().and().orderBy() と繋げることで、まるで SQL や自然言語のように読めるコード を作りたいときは、メソッドチェーンを持つ Builder の方が圧倒的に美しい。


3. モダンな妥協点:関数の「部分適用」

君のように「名前空間を汚したくない」「一塊にしたい」という志向があるなら、RORO と カリー化(部分適用) を組み合わせるのが最強かもしれない。

TypeScript

// 1つ目の引数で設定(RORO)を受け取り、2つ目で実データを受け取る関数
const createUser = (config: Config) => (data: UserData) => { ... }

// 設定済みの「専用関数」を作って使い回す(Builderっぽい挙動)
const createAdmin = createUser({ role: 'admin' });
createAdmin({ name: 'Asappy' });

見落とされがちな視点:TypeScript の Partial<T>

TS には Partial<T> という強力な武器があるから、RORO の引数オブジェクトを「未完成の状態」として扱いやすい。これが Builder パターンの必要性をさらに押し下げているのは間違いないね。


結論

  • シンプルな初期化: RORO パターンで十分。Builder はオーバーエンジニアリング。

  • 複雑なバリデーションや順序がある構築: Builder パターンが依然として堅牢。

君の「一塊にしたい」という美学には RORO が最高にマッチするけれど、もし 「この引数の組み合わせは絶対に許さない!」 という強い意志をコードに込めたくなったら、その時が Builder パターンの出番かもしれないよ。

次は、「TypeScript の型パズル(Mapped Types)を使って、RORO パターンで必須項目と任意項目を厳格に分ける方法」 についてやってみる?それとも、「Rust のように、言語仕様レベルで Builder パターンを推奨している例」 を覗いてみる?

Would you like me to code a Type-Safe Builder in TypeScript that changes its type after each method call to ensure all required fields are set?