クラスを作ることをサボらない

JJUG CCC 2018 Fallのいくつかのセッションで型についての話があったので書きたくなった。
この話はJavaScalaみたいな静的型の世界の思想で、多分Rubyとかでは違う思想だとおもう。

Javaを書いてるときのおれきゅーの脳内

型を定義して取りうる値を狭めることで 間違った使い方ができないようにする ことを最初に考えてる。多分。防御的プログラミングってやつなのかな?しらんけど。
とにかくエラーはより早いタイミングで見つかるほうが良くて、実行時よりコンパイル時にわかるほうが便利だし、コンパイル時よりエディタ上でリアルタイムに分かるほうがもっと良い。

たとえば汎用的な型を使うとこんなミスがあり得る。

long id = 10;
new User(id);
new Item(id);

静的型なら多分こうかけるほうが間違えなくて嬉しい。

Id<User> id = Id.of(10);
new User(id);
new Item(id); //コンパイルエラー

型に意図をもたせる

これはirofさんのセッションに出てきた話で、突然String strという変数が出てきたとき、これが何なのか、どんな操作が許されているのかがわからない。Stringという型には文字列以上の情報が含まれていなくて、splitしたりsubstringできたりする。仮にこれがDisplayNameという型であれば、splitやsubstringなんて操作はないし、表示用に整形するだとかDisplayNameとしての操作だけが提供されて間違えることはない。
コンストラクタやfactoryメソッドで入力値をチェックすることで例えば空文字の場合はインスタンス化できないようにすると、DisplayNameの取りうる値の範囲が制限されてDisplayNameを扱うときに考えなければいけないことを減らすことができる。

Railsにはcomposed_ofというあまり使われていないマイナーな機能があってそれを好んで使っている。これはDBのカラムの値を値にマッピングする機能で、JPAでいうEmbeddedと同じような機能。Rails界隈であまり使われていない機能でも好んで使うのは取りうる値の制限ができるという理由から来ている。
宣伝ですが、composed_ofについては技術書典5で頒布した「pixiv PAYの薄い本」で書いてある。ちなみに表紙は僕が一番好きなイラストレーターのしらたま先生です。

booth.pm

Folioという証券会社のScalaの事例では、金額などの計算に型を使うことでうまく意図をもたせていた。

val yen = JPY(100)
yen * 5 // JPY(500)
yen * yen // JPY同士の掛け算は定義していない。そんな意味不明な計算はありえない。
yen / 5 // 割り算とかもありえないのでコンパイルエラー

これはわかりやすい例なのでそんなことでミスしないだろうと思いがちだが、ドメイン知識が複雑になると力を発揮する。
別の例で、損益と損益の引き算の例では以下のようなコードが示されていた。

/** 実現損益と含み損益をあわせて損益 */
case class PnL(unlialized: UnlializedPnl, realized: RealizedPnl) {
  /** 損益の差は損益ではなく損益の差 (例えば前日比)*/
  def -(other: PnL): PnlDiff = PnlDiff(this.value - other.value)

  def value: JPY =  unlialized.value + realized.value
}

計算結果の型によって計算の意図が読みやすくなるし、型が明確に分かれることで取り違いがなくなったり、気をつけたり意識することの脳内リソースを開放できる。

クラスはどれだけ作れば良いか

別の概念のものなら息をするように作る細かく分ける。
名前考えるのは面倒だけど、作るのは一瞬。ずっと使うものだしどんどん作ろう。プリミティブは滅ぼそう。
とにかく最初はやりすぎかな?って思うくらいやればいい。分けるのは大変だけど統合は簡単。しらんけど。とりあえずやってから考えよ。

おれきゅーのコードを書くときの心構え

息をするようにクラスを作る。名前を決める。
クラスを作るのをサボらない。


参考

コードをどまんなかに据えた設計アプローチ - Speaker Deck

Scala と Microservices でつくる 証券会社とスタートアップ / FOLIO in JJUG CCC 2018 Fall - Speaker Deck