第8章 実装を隠す
本章でやったこと
サブクラス(Dollar と Franc)の times メソッドの処理が重複する問題を解消したい
しかし、そのまま解消するにはステップが大きいので、以下のように少しずつ TDD で進めた
times メソッドのシグネチャを統一した
親クラス(Moeny)を 抽象クラス とし、times を 抽象メソッド として定義した
Factory Method pattern を導入し、テストコードからサブクラスの存在を隠した
これにより、既存コードに影響を及ぼさずに継承構造を変更することが可能 に
結果、テストが冗長になったが、今はそのままとした
気になったこと
Dollar と Franc は現実世界では別モノであるのに、共通化を進めようとしている
「重複を避けるため」だけではちょっと弱くないか?
現実世界では別モノ
明かに別通貨であり、為替レートも違えば法的な位置付けも違う
親子間の関係性は厳密に is-a の関係だったが、子同士は is-a の関係ではないことは明確
第7章 疑念をテストに翻訳する でも「 equals が Dollar と Franc で内部値が同じならば true となってしまう」ということを問題視していたはず
今の実装を俯瞰してみる
現時点ではこれらのクラスがモデリングしているのは通貨そのものではなく、振る舞い(金額の計算)
こう考えるとまだやってることが理解できそう
いや、それなら Interface を切るべきじゃないか?
もちろん 早すぎる抽象化 は良くないが…
多分後の章でインタフェース出てくるのかな?
そもそも、テスト 駆動開発 なのでこういうモデリングも必要に駆られたタイミングでやればいいのかなと落ち着いた
大切なのはテストを書いて → 最低限の実装 → リファクタリング のサイクルを回す
詳細なロジックや設計もそのサイクル内から生まれる(Clean Craftsmanship)ので焦らない
Factory Method でテストコードを置き換えている箇所があるが、これって置き換え忘れても気がつけなくない?
Java で Factory Method を強制させる(コンストラクタを潰す)ことってできるのかな
private メソッドにすると親クラスから呼び出せなくなるので 🙅
private なネストクラスにするのかな
Scala だと コンパニオンオブジェクト 使って楽に表現できた気がする
Claude Code に書かせてみた 🤖
code:scala
sealed trait Money:
def amount: Int
object Money:
private case class Dollar(amount: Int) extends Money:
private case class Franc(amount: Int) extends Money:
def dollar(amount: Int): Money = Dollar(amount)
def franc(amount: Int): Money = Franc(amount)
Scala 独自の機能解説
sealted trait: 同一ファイルからのみ mixin 可能な トレイト
object: シングルトンオブジェクト の定義
case class: イミュータブル なデータを表現するクラス
Scala3の機能をガッツリ使うとすると、enum とか Opaque types 使うのかな…
沼なので一旦停止
スマートコンストラクタ
#読書メモ