パターンは繰り返し現れる設計上のトレードオフに名前を付けます。Java の言語機能でパターンを実装できますが、 ラムダも依存性注入コンテナーも、それ自体が設計判断の代替になるわけではありません。
生成パターン #
Factory は、呼び出し側が具体型に依存してはならない場合や、構築時に実装を選択する場合に生成処理を集約します。 ファクトリは有用な詳細を隠しますが、製品ごとにメソッドを持つファクトリは不要な間接層になる可能性があります。
interface Parser { Document parse(String text); }
final class Parsers {
static Parser forFormat(Format format) { return format::parse; }
}Builder は任意引数を集め、不変値を生成する前にフィールド間の不変条件を検証します。
必須フィールドが少ない場合はコンストラクターまたは static ファクトリを使います。setter をそのまま反映するだけのビルダーは必要状態を分かりにくくします。
Prototype は、浅いコピーと継承の意味論を安全に拡張しにくい Cloneable より、
明示的なコピーコンストラクターまたは copyOf ファクトリとして表す方が Java では明確なことが多いです。
構造パターン #
Adapter は外部インターフェースをアプリケーション所有の契約へ変換します。 変換を保守するコストはありますが、ベンダー型がドメイン層全体へ広がるのを防ぎます。 Decorator は同じインターフェースの周囲に動作を追加します。
final class MeteredStore implements Store {
private final Store delegate;
public Value get(Key key) {
metrics.recordAttempt();
return delegate.get(key);
}
}キャッシュ、認可、メトリクスなどの関心事が独立して変化する場合、サブクラスの組み合わせよりデコレーター合成が適しています。 Java の I/O ストリームは典型例です。Facade と Composite も、意図的に複雑なサブシステムを簡単にしたり、 全体と部分のツリーをモデル化したりする場合に同様に役立ちます。
振る舞いパターン #
Strategy は安定したインターフェースの背後でアルゴリズムを選択します。Java ではインターフェース実装、 関数型インターフェース向けのラムダ、閉じた戦略集合向けの enum 定数で表現できます。
enum Rounding {
DOWN { BigDecimal apply(BigDecimal n) { return n.setScale(0, RoundingMode.DOWN); } },
UP { BigDecimal apply(BigDecimal n) { return n.setScale(0, RoundingMode.UP); } };
abstract BigDecimal apply(BigDecimal n);
}Observer はリスナーへイベントを公開します。同期・非同期配信、順序、障害分離、バックプレッシャー、リスナーの所有権を指定してください。 指定しないと、オブザーバーリストが隠れた制御フローになります。Command、Template Method、Iterator、State パターンは、 大きな条件分岐なしにそれぞれ変化を明示できる場合、依然として有用です。
依存性注入はオブジェクトの外部から協調オブジェクトを提供します。ファクトリ、戦略、デコレーター、オブザーバーを接続できますが、
それらの境界を選ぶものではありません。同様に、record、sealed 型、switch、ラムダは定型コードを減らしますが、
オープンな多相性と閉じた集合のどちらがドメインを表すかを決定するものではありません。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)