LoginSignup
9
7

More than 5 years have passed since last update.

Scalaでたぶん一番簡単なDI

Last updated at Posted at 2015-09-04

Outline

ScalaでDIをちゃんと考えてみようと思い、ここを参考にcakePatternとか色々調べてみました。
http://eed3si9n.com/ja/real-world-scala-dependency-injection-di

でも全体的に何が嬉しいのか見えなかったので、手段から入るのをやめて、
何ができれば嬉しいのかを明確にしてから一番シンプルにそれが達成できそうなものを考えてみました。

Premise

SpringMVCの設計みたく、

Controller→Service→Repository

の依存関係があるとします。
また、今回はRDBを扱い、トランザクションの管理はServiceでやるとします。

Goal

・各Objectで、mockを差し込んだ単体テストが容易に行える。
・UI確認用に、configを書き換えると実行時にMockのServiceが動くようにする。
・(上記を実現しつつ、ボイラ−プレートが少なく、シンプルな構成になるように。)

How

lazy valで定義したフィールドにMockをoverrideする。

Code

class MyController {
    lazy val myService:MyService = MyFactory.getMyService()
    def action() = println(myService.process())
}

object MyFactory{
    def getMyService() = {
        new MyServiceImpl

        //      if(Config.mode = "Prod"){
        //          new MyServiceImpl
        //      }else{
        //          new MyServiceMock
        //      }

    }
}

trait MyService {
    def process(): String
}

class MyServiceImpl extends MyService{
    lazy val pool = new ConnectionPool
    lazy val myDao = new MyRepository

    override def process() = {
        implicit val con = pool.getConnection()
        myDao.fetchData
    }
}

class MyServiceMock extends MyService{

    override def process() = {
        "mock"
    }
}

class MyRepository {
    def fetchData(implicit dbCon: String): String = {
        "aa from " + dbCon
    }
}

class ConnectionPool{
    def getConnection() = "real Connection"
}


本番時はこのように。

object Main {

    def main(args: Array[String]) {
        val controller = new MyController()
        controller.action()
    }

}

テスト時はこのように。

class Test extends FunSuite{

    test("testMyController") {
        val controller = new MyController(){override lazy val myService = new MyService {
            override def process(): String = "mock"
        }}
        controller.action()
    }

    test("testMyServiceImpl") {
        val mockDao = new MyRepository {
            override def fetchData(implicit dbConnection: String): String = "mock Fetch"
        }

        val poolMock = new ConnectionPool {
            override def getConnection() = "mock Fetch"
        }
        val service = new MyServiceImpl{
            override lazy val pool = poolMock
            override lazy val myDao = mockDao
        }
        assert(service.process() == "mock Fetch")
    }

    test("testMyDaoImpl") {
        val repo = new MyRepository
        val mockDBCon = "mock"
        assert(repo.fetchData(mockDBCon) == "aa from mock")
    }

}

Conclusion

具象クラスのコンストラクタに依存するのがキモい。。
でもそれ以外は問題ないのでは?

cakePatternとかはどういう時に真価を発揮するのかとか、誰かマサカリください。

9
7
12

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7