エリック・エヴァンスのドメイン駆動設計を読みました②

前回の投稿

第4部 戦略的設計

システムがだんだん複雑になってくると、巨大なモデルを扱ったり把握する必要が出てくる。
そうした状況下では単一の巨大なユニットとして管理するのは難しいため、分解しなければならない。
ここで、小さなシステムに分解していくときに、陥りがちなのが全体が見えなくなること。
「木を見て、森を見ず」と述べられている。

そうならないように以下の3つのテーマを考察している

  1. コンテキスト
  2. 蒸留
  3. 大規模な構造

■コンテキスト
コンテキストについて見ていく前にそもそもコンテキストってなんだって感じですよね。
androidの開発をやっていても当たり前のように出てきます。
辞書を引いても何とも曖昧でややこしいです。

ロングマン先生に依ると

1 the situation, events, or information that are related to something and that help you to understand it
2 the words that come just before and after a word or sentence and that help you understand its meaning

とあります。
日本語でも文脈、前後関係、事情、背景、状況と訳されたりしています。

少し具体的な例を挙げると、
私がテレビを見ていてそこには芸能人が映っているとします。
そこで近くにいる家族が
「ちょっと手伝って~」
と言ったときに、テレビに映っている芸能人に言っているとは思わないですよね?
手伝えるのはテレビを見ている私の方です。
これはコンテキストに依るものだと思います。

もう1つ挙げると…
「ほげ」
と聞くと何を思い浮かべますか?
プログラミング経験がある人やIT系の人だとサンプルコードなどでよく見かける
「hoge」
が浮かびますよね?
そうでない人だと
「何かのリアクション?」「捕鯨?」
みたいなこと思い浮かべるかもしれません。

商品と聞いて大体の人は商品そのものを浮かべるけど、配送業の人だと中身が入った段ボールのことを思い浮かべるみたいなことを聞いたことがあります。

同じ用語でも、受け取り手や場面などによって認識が変わってくるのはコンテキストに依るものだと認識しています。

コンテキストの内容に入っていきます。

境界づけられたコンテキストによって各モデルが適用できる範囲を定義する。
コンテキストマップによってそれぞれのコンテキストとコンテキスト間の関係性を全体的に概観できる。
継続的な統合によってモデルの統一が保たれる。

・境界付けられたコンテキスト
名前の通りコンテキストはあいまいでなく明示的に境界付ける。

明示的な境界は、チーム編成、そのアプリケーションに特有の部分が持つ用途、コードベースやデータベーススキーマなどの物理的な表現などの観点から設定する。
その境界内では、モデルを厳密に一貫性のあるものに保つ、また外部の問題によって注意をそらされたり、混乱させたられたりするのは避ける。

定義することで得られるものは明確さ。
そのコンテキスト外の人達は、コンテキスト内の事は明確に区切られているのであれば考えなくて済む。
ただし、境界を知っておかなければ、知らずに踏み込んでしまう可能性があるからそれは注意が必要。
接点があるコンテキスト間も同様。

・継続的な統合
境界づけられたコンテキストを定義したらそれを安定させなければならない。
継続的な統合はコンテキスト内で、2人以上での作業をする際には頭に入れておく。

絶えずそのコンテキストチーム内で以下2つの統合を行う。

  1. モデル概念の統合
    ユビキタス言語を継続的に練り上げる。そのためにメンバ間で絶えずコミュニケーションを行う。
  2. 実装の統合
    段階的に行われ、再生可能なマージ、ビルドテクニック
    自動化されたテストスイート
    変更が統合されない期間に対して、妥当な短さの上限を設定するルール

・コンテキストマップ
コンテキストを境界づけても全体的な視点を得られたとは言えない。
境界づけた時点での全体的視点は得られているかもしれないが、それぞれのコンテキスト内で統合を絶えず行っているため、得られているとは言い難い。
変更により境界が明示的でなくなる場合がある。
境界づけられたコンテキスト間のコードの再利用もそういった理由で避けるべき。

全体的な視点を得るためにコンテキストマップを利用する。
コンテキストマップの利用にあたり、以下のルールを守る
境界づけられたコンテキストにそれぞれ名前をつけ、その名前をユビキタス言語の一部にする
現在存在する領域の地図を書く、変換にはそのあと取りかかる
モデル同士の接点を記述して、あらゆるコミュニケーションで必要となる明示的な変換について概略を述べ、共有するものがあれば強調する
マップは常にありのままの状況を表すこと
実際に変更が完了するまではマップを変更しないこと

コンテキスト内であればユビキタス言語が方言化(同じ言葉でもコンテキスト外の人が使っているものとニュアンスの差異があることがある)することもあるが、境界づけられたコンテキストそのものに名前をつけることでどのコンテキストで作業している人でもあいまいにならずに済む。

これらが保たれると以下のより効果的な戦略へと移行できるようになる。

1. 共有カーネル
2つのコンテキスト間(あるいは複数のコンテキスト間)でモデルの一部を共有すること。
カーネルというとそれぞれのモデルの中核を共有するようにとらえるかもしれないが、モデル、コード、モデルの該当箇所に関連するデータベース設計なども含む。
注意点としては、共有している部分は共有していないコンテキスト内の変更に比べて頻度は下げること、他チームの相談なしに行わないこと。

2. 顧客(下流)/供給者(上流)の開発チーム
2つのコンテキストA, Bがあって、BがAからの入力を受け取るなどAがBに何かを要求する場合に顧客/供給者の関係を取ることがある。
A(供給者) → B(顧客)
この場合に、BがAの変更に対して、一方的に受け入れるだけならば簡単だが、Bが拒否権を持っていたりやAに対して何か手続きをさせるような場合は両者が円滑に作業できるようにしなければならない。
そのために…
・顧客の要求が最優先
・供給者チームが顧客を壊してしまわないかと恐れることなくコードを変更できるようにし。顧客チームが上流チームのことを常に気にすること自分の仕事に集中できるようにするには、自動化されたテストがないといけない。

3、4、5は顧客/供給者の関係にありながら顧客の要求に応える動機がない場合、2のことができないので検討する。

顧客の要求に応える動機がないというと攻撃的だが以下のような場合もある
・供給者が多くの顧客を抱えている
・2つのチームが経営階層上で遠く離れている
・昔からの顧客に市場の方向性が変わったため価値が見いだせない
など

3.順応者
一言でいうと上流チームに順応する。
上流チームのモデルに隷従しユビキタス言語を供給する。
下流チームの自由度は低くなるがシンプルになる。
下請けとかこんな感じかなと思う。
上流チームの設計が悪いと下流チームも悪くなるのでその辺りは気を付ける。

4.腐敗防止層
上流のモデルを下流で扱うのが困難な場合に検討する。
上流と下流の間にモデルの変換機能など腐敗防止層を用意してあげる。
そのインターフェースはデザインパターンのファサードパターンやアダプタパターンなどで構成されたりする。
他のシステムとの連携でデータを変換する出入り口とかがイメージしやすい。

5.別々の道
上流と下流の関係だったが、そもそも別々で良い場合など。
これまで学んできた上流と下流の統合方法は全てオーバーヘッドが存在するため、コストが発生する。
よって、それに見合う利益を生み出さないといけない。
利益を生み出せない場合などに、統合が必要なのかを改めて考える。
それでも必要な場合は1~4のどれかを考える。
とりあえず別々の道を検討して無理または面倒ならば何らかの形で統合するとかでいいかも

6. 公開ホストサービス
とあるサービスが他の多くのサービスなどから利用される場合は、共通のサービスを用意する。
手間なので1つ1つについて変換サービスを構築しない。
特定のサービス向けに拡張することはある。
公開APIなど。

7. 公表された言語
統合するお互いのコンテキスト間の変換では共通する言語を利用する。
片方に合わせるとモデルが崩れる恐れがある。
世に回っているドキュメントなどで広く使われているものなどを見つけれるといい。
この辺なんとなく上流に合わせがちになっている気がする…

実際にこれらのパターンをどう使っていくかですが、
新しくプロジェクトを始めるにしろプロジェクトが進行中にしろ
コンテキストマップを描こうとすることが大事になってくると思います。
そのためにはコンテキストの境界を付けること、継続的な統合を行う。
その後は変換できないシステム(外部システムやレガシー過ぎて手がつけられないシステム)に線を引く。

・新しくプロジェクトを始めている場合
作成中のシステム内で継続的な統合が困難になれば共有カーネルを探す。
依存関係が一方向であれば顧客/供給者の開発チームを選択する。

外部のシステムと連携する場合は最初に検討すべきは別々の道。
コストが低いので。
統合が不可欠であるなら、順応者か腐敗防止層かを考える。
出来るだけ腐敗防止層で考えたい。
順応者は心情的によろしくないので。

・プロジェクトが進行中の場合
まず共有カーネルを見つける。
コンテキストの重複した出来るだけ小さいところから始めていく。
テストは必ず作成する。

次に継続的な統合を行い両方を理解した人を増やす。
コンテキスト間でメンバーをローテーションする。

その後にレガシーシステムを段階的に廃止する。
テスト戦略の決定は不可欠で腐敗防止層を経由してレガシーシステムと通信する

公開ホストサービスがあるなら公表された言語が使えないか検討する。

エリック・エヴァンスのドメイン駆動設計を読みました①

難解と言われながら名著とされている「エリック・エヴァンスのドメイン駆動設計」を読みました。

備忘録として自分なりの解釈を入れたりしてメモを残しておきたいと思います。
間違いもあると思うのでご指摘ください。
この記事では3部まで記載しています。

第1部 ドメインモデルを機能させる
ドメインモデルとは何かですが、それぞれドメインとモデルを以下のように認識しています。

ドメイン
プログラム、システムが関心を持つ領域や対象

モデル
物や事象、概念などを現実にあるものや現実で行っていることをベースに必要な部分を厳選して抽象化すること

モデルの必要な部分というのがドメインによって変化するので、ドメインによって精錬されたモデルがドメインモデル。

ドメインモデルを機能させるために…
・知識をかみ砕く
大量の情報から重要な情報を抜き出す、重要な言葉を見つけ出す(1人で行うのはNG) 抜き出すためには何が重要な情報かを見極めるためにその分野の学習が必要になる

・コミュニケーションと言語の使い方
チームで誤解のないように共通の言葉(ユビキタス言語)を使いながらモデル化を行う。
もし名前がなければ付ける。
その名前の物が何をするのか、チーム全体で共有する。
それが共有できないとコードの共有をするのも難しくなる。

・モデルと実装を結びつける
モデル駆動設計を行う。手続き型ではない。

モデル駆動設計
ソフトウェア要素のサブセットがモデル要素と密接に対応している設計。
相互に一致した状態を保つ。

第2部 モデル駆動設計の構成要素
・ドメイン駆動設計がアーキテクチャに求めること
レイヤードアーキテクチャなどドメイン層が分離したもの

・ドメインモデルの構成要素
ドメインモデルはエンティティと値オブジェクトとドメインサービスから構成される。

エンティティ…同一性(IDなどの識別子を持つ)を持つオブジェクト
値オブジェクト…エンティティとは逆で同一性がないオブジェクト。
同じ色で同じマジックが2つあったらどちらを使うのか気にしない

※同じモノでも視点や誰が使うかによって、同一性をもつかどうかは変わるので注意

ドメインサービス…エンティティや値オブジェクトとして扱うと不自然なもの
エンティティと値オブジェクトもドメインサービスとして扱うこともできるので節度を持って扱う

優れたサービスは以下の3点

  1. 操作がドメインの概念に関係しており、その概念がエンティティや値オブジェクトの自然な一部ではない。
  2. ドメインモデルの他の要素の観点からインターフェースが定義されている
  3. 操作に状態がない

第3部 より深い洞察へ向かうリファクタリング
リファクタリングはソフトウェア開発者にとっては良く知られている言葉で、機能の変更をしないようにソフトウェアの再設計を行うこと。
マーチン・ファウラーの著書である「リファクタリング」のような、ざっくり言うとコードをきれいにするのも重要だが、ドメインモデルの場合は、いわゆるクックブックのやり方を当てはめるだけでは済まない。
クックブックを当てはめることは良いことではあるが、当てはめてドメインモデルが改悪されてしまうと意味がなくなる。
適切なドメインモデルを念頭に創造力を持つこと、試行錯誤を繰り返すことがまず第一で、それが外れない範囲でパターンを適用する。

・ブレイクスルー
ブレイクスルーは起こすものではなくて結果的に起こるもの 継続的なリファクタリングをすることによって、コードやモデルが整えられる。
改良するたびに、開発者の視界は明確になってくる
視界が明確になったことにより、洞察のブレイクスルーをもたらす可能性が作り出される

ブレイクスルーの舞台を整えるために必要なこと

  1. 知識を噛み砕き、強固なユビキタス言語を育成するのに集中すること
  2. 重要なドメインの概念を探求して、それをモデルで明示すること
  3. 設計をよりしなやかになるように改良すること
  4. モデルを蒸留すること

・概念を掘り出す

ドメインエキスパートの使う言葉に耳を傾ける 以下のモデルにとって有益になりうる概念を示す手掛かりを見逃さないようにする。

  1. 何か複雑なものを簡潔に述べている用語がないだろうか?
  2. ドメインエキスパートに言葉の選び方を(たぶん、角が立たないように)正されていないか?
  3. あなたが特定のフレーズを使った時に、ドメインエキスパートたちの困惑した表情が消えることはないか?

制約やプロセスなどをモデル概念とすると設計が鋭くなる場合がある。

・より深い洞察へ向かうリファクタリング 重点的に取り組むべきこと

  1. ドメインに馴染む
  2. 常に物事に対して違う見方をする
  3. ドメインエキスパートとの会話を途切れさせない

リファクタリングを行う際はコードが整然としていることで安心しない。 ドメインモデルが適切かを常に考える必要がある。

モデルが適切でなかった場合、良いモデルについて探求する必要がある。 探求するには、より長い時間がかかり、より多くの人の参加が必要となる。 ドメインエキスパートや元々の開発者など、より多くの人とミーティングを行う。

ソフトウェアはユーザーのためだけのものではなく開発者のためのもの。

より深い洞察へ向かうリファクタリングは継続的なプロセス。 暗黙的な概念が認識されて明示的になる。 設計の一部はよりしなやかになる。 そして深いモデルへと突き進みまたリファクタリングを繰り返していく。

Effective Javaを読みました

Javaを扱っている人のバイブル的な本ですね。

今まで読んだ本について各項目について書いておりましたが、Effective Javaについては量が多いのでざっくりとした感想だけ書きたいと思います。

第3版を読みました。第2版と比較しましたが、ラムダとストリームという章が追加され、その他の章は大きくは変わってなさそうな印象でした。第2版はもちろん読んでません。

◾️追加された項目
第2章 オブジェクトの生成と消滅
 項目5 資源を直接結び付けるよりも依存性注入を選ぶ
 項目8  ファイナライザとクリーナーを避ける
 項目9 try-finallyよりもtry-with-resourcesを選ぶ

第4章 クラスとインターフェース
 項目21 将来のためにインターフェースを設計する
 項目25 ソースファイルを単一のトップレベルのクラスに限定する

第7章 ラムダとストリーム(※全て追加)
 項目42 無名クラスよりもラムダを選ぶ
 項目43 ラムダよりもメソッド参照を選ぶ
 項目44 標準の関数型インターフェースを使う
 項目45 ストリームを注意して使う
 項目46 ストリームで副作用のない関数を選ぶ
 項目47 戻り値型としてStreamよりもCollectionを選ぶ
 項目48 ストリームを並列化することは注意を払う

第8章 メソッド
 項目55 オプショナルを注意して返す

第11章 並行性
 項目80 スレッドよりもエグゼキュータ、タスク、ストリームを選ぶ

第12章 シリアライズ
 項目85 Javaのシリアライズよりも代替手段を選ぶ

◾️感想
正直、Javaを軽くしか触っていない自分にとっては難しいところが多々ある本でした。
といっても、メソッドやプログラミング一般などは比較的な簡単な項目があったりしたので、全部が難しいわけではないです。

この本に関して初心者に読んでほしい、初心者には向かないなど色々な意見がありますが、自分は初心者の人に関しては、読み方を気をつければ読んだほうがいいかと思います。
罠にハマらないような書き方、どうすると罠にはまってしまうのかなども記載されているため、早めに直しておきたい項目が多々あるからです。
読み方としては項目毎に軽く2ページほど読んで、理解できそうかどうか、あるいは頑張れば理解できそうだったらしっかりその項目を読んで、わからなかったところは時間が経ってから読み直すといったやり方がいいと思います。
デザインパターンを知らないのに解説もされていないデザインパターンについて言及されている項目が理解できるはずがないと思います。
そういうのが多くある場合、挫折すると思うのでここは後回しでいいと割り切っていくのがいいかと思います。
自分も読んでいて心折れかけそうなときがありました。
ですので、わからなかったときに何を勉強しておくとわかるようになるのかをメモしておいてそれについて勉強してから読むぐらいの認識でいいと思います。

内容についてですが、凄く勉強になりました。
例えば 第2章のすべてのオブジェクトに共通のメソッドについてはお恥ずかしながらJava標準でのメソッドをオーバーライドをするという発想が皆無だったのでtoStringをオーバーライドするなどは新鮮でした。
第5章のジェネリック型はよく使ってますがやっぱり便利だと感じます。ArrayListがPHPにも欲しい。
第6章のenum、第7章のラムダとストリーム、第11章の並行性はぜひ使いこなしたいです。
誤解かもしれないですが継承はもうあまり使わない方がよかったりするのかな?最近はis-aよりhas-a傾向強い気がします。

この本に関しては手元に置いておいて必要な時に何度も読み返したいと思います。

Prototypeパターン

Prptotypeパターンは生成に関するデザインパターンの1つです。 通常はnewしてインスタンスを生成しますが、Prototypeでは既存のインスタンスをコピー(clone)して新しいインスタンスを生成します。

メリット
新しいインスタンスを作成する複雑さを、クライアントから隠蔽する。
型のわからないオブジェクトの生成を行うという選択肢を、クライアントに提供する。
環境においてはオブジェクトのコピーの方が新しいオブジェクトを作成するよりも有効である可能性がある(ソースコードの管理等)。

デメリット
コピーを作る事で物事が複雑になる場合がある(これについては追々書いてみたい)。

Head First デザインパターンでモンスターを作るクラス図があったのでそれを参考に中身だけ書いてみたいと思います。
単純にモンスターをコピーできるようにするのと、ゲームを楽しむユーザーがモンスターのレベルとHPを変更できるようにしていきます。

・Monster.php

<?php
abstract class Monster
{
    private int $id; // 1種類につき与えられるモンスターのid
    private string $name;
    protected int $level;
    protected int $hp;
    private string $category;
    private bool $copied; // コピーされたものかどうか

    public function __construct(int $id, string $name, int $level, int $hp, string $category)
    {
        $this->id = $id;
        $this->name = $name;
        $this->level = $level;
        $this->hp = $hp;
        $this->category = $category;
        $this->copied = false;
    }

    abstract protected function __clone();

    public function newInstance(): \Monster
    {
        return clone $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getLevel(): int
    {
        return $this->level;
    }

    public function getHp(): int
    {
        return $this->hp;
    }

    public function getCategory(): string
    {
        return $this->category;
    }

    public function getCaller(): string
    {
        return $this->caller;
    }

    protected function copied(bool $copied): void
    {
        $this->copied = $copied;
    }

}

ユーザー側で値を変更できないモンスター
・WellKnownMonster.php

<?php
require_once 'Monster.php';

class WellKnownMonster extends Monster
{

    protected function __clone()
    {
        $this->copied(true);
    }

}

ユーザー側でlevelとhpを変更できるモンスター
・DynamicPlayerGeneratedMonster.php

<?php
require_once 'Monster.php';

class DynamicPlayerGeneratedMonster extends Monster
{
    protected function __clone() {
        $this->copied(true);
    }

    public function setLevel(int $level): void
    {
        $this->level = $level;
    }

    public function setHp(int $hp): void
    {
        $this->hp = $hp;
    }
}

・MonsterRegistry.php

<?php

class MonsterRegistry
{
    private array $registered;

    public function register(Monster $monster): void
    {
        $this->registered[$monster->getId()] = $monster;
    }

    public function getMonster(int $id){
        return $this->registered[$id]->newInstance();
    }
}

・ index.php

<?php
require_once 'MonsterRegistry.php';
require_once 'WellKnownMonster.php';
require_once 'DynamicPlayerGeneratedMonster.php';

$monsterRegistry = new MonsterRegistry();
$monster1 = new WellKnownMonster(1, 'ゴブリン', 4, 12, 'ゴブリン');
$monsterRegistry->register($monster1);
$monsterCopy1 = $monsterRegistry->getMonster($monster1->getId());

var_dump($monster1);
var_dump($monsterCopy1);

$monster2 = new DynamicPlayerGeneratedMonster(37, 'アイスボム', 24, 400, 'ボム');
$monsterRegistry->register($monster2);
$monsterCopy2 = $monsterRegistry->getMonster($monster2->getId());
$monsterCopy2->setLevel(99);
$monsterCopy2->setHp(9999);

var_dump($monster2);
var_dump($monsterCopy2);

コピー元となるモンスターはDBに入れそうなものですが…。 その場合は引っ張ってきてインスタンス化して必要であればコピーしていくといった方法で使うといいかな?って思います。

(参考)
Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
2005/12/2
Eric Freeman (著), Elisabeth Freeman (著), Kathy Sierra (著), Bert Bates (著), 佐藤 直生 (監訳), 木下 哲也 (翻訳), 有限会社 福龍興業 (翻訳)
13章 付録:残りのパターン

Builderパターン

Buliderクラスについてサンプルを書きます。
effective javaに書いているようなBuilderとGoFのBuilderは違うのかな?その辺がよくわかりませんが、GoFの方を書いていきます。目的は一緒だと思います。
effective javaのBuilderは下をご覧ください。

Bulderパターンですが生成に関するパターンで複合構造の構築に良く使われます。
監督者が(director)、指示を出して建築者 (Builder) が構築していくイメージです。

メリット
複雑なオブジェクトの構築方法をカプセル化できること

デメリット
構築がめんどくさ(ry

他にも複数の手順の変化するプロセスで柔軟なオブジェクトを作成できる点(Factoryとは対照的)がメリットとして挙げられますね。思っていたのと違うパラメータを設定している可能性もあるので一長一短ですが。

それでは作っていきます。夕飯を作ります。

パターン1:ステーキ定食(ご飯、ステーキ、オニオンスープ、ポテトサラダ)
パターン2:パン、ハンバーグ、シーザーサラダ

・Builder.php

<?php
interface Builder
{
    public function stapleFood(string $stapleFood):void;
    public function mainDish(string $mainDish):void;
    public function soup(string $soup):void;
    public function salad(string $salad):void;
    public function getResult();
}

・DinnerBuilder.php

<?php
require_once 'Builder.php';

class DinnerBuilder implements Builder
{
    private string $stapleFood;
    private string $mainDish;
    private string $soup;
    private string $salad;

    public function stapleFood(string $stapleFood): void
    {
        $this->stapleFood = $stapleFood;
    }

    public function mainDish(string $mainDish): void
    {
        $this->mainDish = $mainDish;
    }

    public function soup(string $soup): void
    {
        $this->soup = $soup;
    }

    public function salad(string $salad): void
    {
        $this->salad = $salad;
    }

    public function getResult(): dinnerBuilder
    {
        return $this;
    }
}

・Director.php

<?php

require_once 'Builder.php';

class Director
{

    private Builder $builder;

    public function __construct(Builder $builder){
        $this->builder = $builder;
    }

    public function makeSteakSetMeal(): void
    {
        $this->builder->stapleFood("rice");
        $this->builder->mainDish("steak");
        $this->builder->soup("onion soup");
        $this->builder->salad("potato salad");
    }

    public function addStapleFood(string $stapleFood): void
    {
        $this->builder->stapleFood($stapleFood);
    }

    public function addMainDish(string $mainDish): void
    {
        $this->builder->mainDish($mainDish);
    }

    public function addSoup(string $soup): void
    {
        $this->builder->soup($soup);
    }

    public function addSalad(string $salad): void
    {
        $this->builder->salad($salad);
    }
}

・index.php

<?php
require_once 'DinnerBuilder.php';
require_once 'Director.php';

$builder1 = new DinnerBuilder();
$director1 = new Director($builder1);
$director1->makeSteakSetMeal();

var_dump($builder1);

$builder2 = new DinnerBuilder();
$director2 = new Director($builder2);
$director2->addStapleFood("bread");
$director2->addMainDish("hamburg steak");
$director2->addSalad("caesar salad");

var_dump($builder2);

Builderパターンは実はeffective javaで紹介されているような物を良く使っていてあまりこちらで書いていないので他の人がこの書き方で書いているのを見た事があって混乱した覚えがあります。BuilderといってもLombok BuilderやFluent Builderというのもあるみたいです。複雑ですね…

(参考)
Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
2005/12/2
Eric Freeman (著), Elisabeth Freeman (著), Kathy Sierra (著), Bert Bates (著), 佐藤 直生 (監訳), 木下 哲也 (翻訳), 有限会社 福龍興業 (翻訳)
13章 付録:残りのパターン