WebエンジニアのLoL日記

LoLをプレイしたりLJLの試合を見たりするのが好きなエンジニア。LoLのイベントやパッチノートなど気になった点を記事にしたり、LJLについの記事をかいたりしています。某社でWeb系のエンジニアとして働いているので、技術系の記事もたまに書きます。コンタクトを取りたい場合はtwitterまで。

【ScalaMatsuri2018】tototoshiさん「Scalaらしいオブジェクト指向プログラミング」

もくじ

はじめに

ScalaMatsuriってけっこう関数型の発表が多く、オブジェクト指向の発表は少ない。オブジェクト指向の発表はあまりない。しかし、普段コードを書いているとオブジェクト指向の機能を使うことは多い。ので発表してみることにした。

DIの話が多め。

Scalaの言語の性質

Javaとくらべてどうか、ということを考えると以下の2店がある。

  • immutable
  • Composable
  • traitとか

DDDの本を読むと、Java、しかも古いJavaの言語仕様を想定して書かれているので、Scala,にそのまま当てはめるのは微妙である。われわれもScalaをうまく使えるように進化していかなければならない。

class

Scalaのclass

  • ScalaのclassはJavaとほぼ同じ
  • Javaとの互換性も高い

case class

これはScala独自のもの

classとcase classどちらを使えばいい

初心者だと「case classでよくない?」となってしまうがそうとはいかない。case classの透過性だとおかしいって場合はcase classだとおかしい。

trait

Scala独自の機能

  • Interfaceとして使われるのが一番多いのではないか
  • Mixinにも使われる
  • Cake pattern
  • ケーキの材料を組み合わせて作るみたいな
  • classの材料としてのtrait

object

  • シングルトンとして使われる
  • がシングルトンって本当はあんまりよくない
  • Companion object

これらをどう使っていくか

これらを組み合わせてどうやってclassを作っていくか、というのが今回の話。

シングルトンだらけにしてはいけない

Scalaではobjectが乱用される傾向にある。serviceとかdaoとか。

objectにすると何がダメなのか

実装を差し替えられないというのが一番の欠点。使えば使うほど密結合になってしまう。

object UserRepository 
object UserService {
  def get = UserReposutiry.fetch
}

ではどうするのか

DIをうまく使う

コンストラクタインジェクション

class UserRepository {
}

class UserService(userRepositiry: UserReposutory) {
  Def get = userRepository.fetch
}

コンストラクタ引数が多くなるのは依存が複雑になっている合図。

Guiceを使ってコンストラクタインジェクション

class UserRepository {
}

class UserService @Inject (userRepositiry: UserReposutory) {
  Def get = userRepository.fetch
}

getInstance(UserService);

コンストラクタインジェクションの特徴

  • シンプル
  • GuiceをはじめとしてほとんどのDIコンテナが対応
  • Play Framework との相性も良い

Cakeパターン

traitをいっぱい使ってクラスを作っていく

val userService = new UserService with UserRepository

case class

case class は別のクラスとかを持たない。

case class User(id: Int, name: String, repository: UserRepository) {
}

UserRepositoryをもつと透過性的に問題が….。case classには振る舞いを持たせるべきではない。

振る舞いをもたせたい場合は?

implicitで定義する

case class User(id: Int, name: String) {
}

class UserOpts(userRepository: UserRepository) {
  Implicit def RichUser(user: User) {
    def get = userRepository.detch
  }
}

まとめ

  • 自分DI好きだな
  • 何も考えずCakeパターン、何も考えずコンストラクタインジェクション、はだめ
  • 疎結合にしよう

QA

  • 結局DIコンテナあればOKみたいな感じにならない?
    • でもGuiceとか使うとコンパイル時エラーでたりするのであまり良くないなってなるときもある
    • 結局自分でDIしたり