第 8 章: パターンマッチとシールドトレイト¶
8.1 はじめに¶
Scala の match は単なる switch の置き換えではなく、型と条件を統合して表現できる強力な構文です。sealed trait と組み合わせると、代数的データ型(ADT)として安全に分岐を記述できます。
この章では、FizzBuzz のタイプ生成と変換ロジックを題材に、パターンマッチの基本と網羅性チェックを確認します。
8.2 パターンマッチの基本¶
match/case の基本構文は次のとおりです。
def generate(number: Int): String =
number match
case n if n % 15 == 0 => "FizzBuzz"
case n if n % 3 == 0 => "Fizz"
case n if n % 5 == 0 => "Buzz"
case n => n.toString
重要な要素は 3 つです。
match/caseで分岐を宣言的に記述できるcase n if ...のガード条件で複合条件を自然に表現できるcase _やcase nでワイルドカード/フォールバックを定義できる
8.3 シールドトレイトと網羅性¶
sealed trait は「この抽象型の実装は同一ファイル内に限定する」という制約を与えます。
sealed trait FizzBuzzType:
def generate(number: Int): String
この制約により、コンパイラは次を支援できます。
- 実装候補を把握し、網羅性を検証できる
matchで未処理ケースがあると警告できる- 将来の型追加時に、分岐漏れを早期に検知できる
sealed trait は ADT を安全に扱うための土台です。
8.4 ファクトリメソッド¶
タイプ番号から FizzBuzzType を生成する実装です。
def create(typeNumber: Int): FizzBuzzType =
typeNumber match
case 1 => Type01
case 2 => Type02
case 3 => Type03
case _ => throw IllegalArgumentException(s"未定義のタイプ: $typeNumber")
生成責務を FizzBuzzType.create に集約することで、呼び出し側は型番号の解釈を意識せずに済みます。
8.5 Strategy パターンとの対応¶
OOP の Strategy パターンでは、アルゴリズムをインターフェースで抽象化し、実行時に具象実装を差し替えます。
FizzBuzz では次の対応になります。
- Strategy の抽象:
FizzBuzzType(sealed trait) - 具体 Strategy:
Type01/Type02/Type03 - Strategy 選択:
FizzBuzzType.create(typeNumber)
この構成により、呼び出し側の条件分岐を排除し、ポリモーフィズムで振る舞いを切り替えられます。
8.6 テスト¶
ファクトリメソッドは TypeSpec で次のように検証しています。
test("create: タイプ 1 を生成できる") {
assert(FizzBuzzType.create(1) === FizzBuzzType.Type01)
}
test("create: タイプ 2 を生成できる") {
assert(FizzBuzzType.create(2) === FizzBuzzType.Type02)
}
test("create: タイプ 3 を生成できる") {
assert(FizzBuzzType.create(3) === FizzBuzzType.Type03)
}
test("create: 未定義のタイプで例外が発生する") {
assertThrows[IllegalArgumentException] {
FizzBuzzType.create(4)
}
}
正常系と異常系を両方固定することで、生成ルールの変更に対して安全にリファクタリングできます。
8.7 まとめ¶
この章では、Scala のパターンマッチと sealed trait の組み合わせを確認しました。
match/caseとガード条件で分岐を明確化sealed traitで実装範囲を制約し、網羅性チェックを有効化createファクトリで Strategy の切り替えを一元化
次章では、ここまで作成したドメインモデルをパッケージ単位で整理し、モジュール設計としてまとめます。