dependency injection(scala)

  • this

trait User { def name :String }

// :type ,  a trait only consisting an instance of a String
trait B { somename : User => def foo() { println(name) } }
//needn't be somename.name(it must be) ,somename is an instance of trait User object Main extends B with User { override def name = "whj"; } Main.foo
trait Foo{ this => {def close:Unit} } //foo is a function
//This allows for safe mixins for duck-typing.

 

  • how to implement/achieve dependency injection (DI) in Scala

1 Cake Pattern

This pattern is first explained in Martin Oderskys' paper scalable component abstractions as the way he and his team structured the Scala compiler.

2 dummy codes--just split the interface and its implementation

 

class U { def auth(u:U): U = {println("auth u"+ u)  u} def create(u:U) =println("create u"+ u) def delete(u:U) =println("delete u"+ u)}

class S{ 
def auth(uname:String, s:String ): U =uname.auth(u,s) 
def create(uname:String, s:String)=u.create(new U(uname, pass)) def delete(u:U) = All is statically typed. u.delete(u)}

 

 

 

3 interesting parts--wrap the UserRepository in an enclosing trait and instantiate the user repository 

trait UserRepositoryComponent {
  val userRepository = new UserRepository
  class UserRepository {
    def authenticate(user: User): User = {
      println("authenticating user: " + user)
      user
    }
    def create(user: User) = println("creating user: " + user)
    def delete(user: User) = println("deleting user: " + user)
  }
}
//wrap and use a self-type annnotation to declare our need for U service

//using self-type annotation declaring the dependencies this component requires


trait UserServiceComponent { this: UserRepositoryComponent => 
  val userService = new UserService
  class UserService {
    def authenticate(username: String, password: String): User =
      userRepository.authenticate(username, password)
    def create(username: String, password: String) =
      userRepository.create(new User(username, password))
    def delete(user: User) = userRepository.delete(user)
  }
}
//this: Foo with Bar with Baz =>   declared the dependency
  1. all wiring is statically typed =>if one dependency is missing, we get a compilation error
  2. immutable, all dependencies are declared as val =>get the top-level component from the registry and all are wired, like Guice ,Spring

 

object ComponentRegistry extends UserServiceComponent with UserRepositoryComponent

val userService = ComponentRegistry.userService

val user = userService.authenticate(..)

4 have strong coupling between the service implementation and its creation, the wiring configuration is scattered all over our code base

Instead of instantiating the services in their enclosing component trait,  change it to an abstract member field. 

 

object ComponentRegistry extends UserServiceComponent with UserRepositoryComponent{

  val userRepository = new UserRepository

  val userService = new UserServic
}

 5 

trait TestEnvironment extends
  UserServiceComponent with
  UserRepositoryComponent with
  org.specs.mock.JMocker
{
  val userRepository = mock(classOf[UserRepository])
  val userService = mock(classOf[UserService])
}

 

6

class UserServiceSuite extends TestNGSuite with TestEnvironment {

  @Test { val groups=Array("unit") }
  def authenticateUser = {

    // create a fresh and clean (non-mock) UserService
    // (who's userRepository is still a mock)
    val userService = new UserService

    // record the mock invocation
    expect {
      val user = new User("test", "test")
      one(userRepository).authenticate(user) willReturn user
    }

    ... // test the authentication method
  }

  ...
}
  •  Other alternatives

 主要思想,将interface用trait,implement用class,config用object

再class来以struct type为参数,struct里为所有的interface创建val,def一个method来结合所var的interface

injection发生在object Config中,Config除了lazy val所有的implement的class,还有即为injection的lazy war in = new injectionclassname(this)

  • using implicit
  • using Google Guice
trait ServiceInjector {
  ServiceInjector.inject(this)
}

// helper companion object
object ServiceInjector {
  private val injector = Guice.createInjector(
    Array[Module](new DependencyModule))
  def inject(obj: AnyRef) = injector.injectMembers(obj)
}

 

see http://jonasboner.com/

 

 

 

 

 

 

posted on 2017-09-21 01:41  satyrs  阅读(143)  评论(0编辑  收藏  举报

导航