当前位置:网站首页>Cake pattern and dependency injection in scala programming

Cake pattern and dependency injection in scala programming

2021-10-22 10:36:11 Bird's nest

If you are one Java developer , be familiar with Dependency injection Pattern , Deep dependence Spring Frame words , In the use of Scala When doing development , Will ask a question , stay Scala In the world , How to achieve something similar to Spring The dependency injection of the framework ?

Although believers in functional programming think they don't need DI frame , Higher order (high-order) The function is enough . But for those who support both object-oriented programming and functional programming Scala Come on , Dependency injection is a good design pattern for implementing applications .
Cake mode (Cake pattern) yes Scala One way to implement dependency injection .
The cake mode is Scala The father of Martin Odersky In his thesis Scalable Component Abstractions First mentioned in .

What is cake mode ? An informal but vivid explanation is :

  • The cake has many flavors , You can add the corresponding flavor according to your needs . Dependency injection is to add seasoning .
  • The cake has many layers (layer). If you want a bigger cake, you can add more layers .

Let's start with Spring frequently-used User Take the implementation of database reading as an example , have a look Scala Style dependency injection ( Cake mode ) How is it realized .

Java/Spring Dependency injection implementation of style

A traditional Spring Implementation is to divide the program into "Repository" layer (DAO layer) and Service layer .

       
       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
       
       
       
case class User(name:String)
trait UserRepository {
def create(user: User)
def find(name: String)
def update(user: User)
def delete(user: User)
}
class MockUserRepository extends UserRepository {
def create(user: User) = println( "creating user: " + user)
def find(name: String) = println( "finding user: " + name)
def update(user: User) = println( "udating user: " + user)
def delete(user: User) = println( "deleting user: " + user)
}
       
       
       
1
2
3
4
5
6
7
8
9
10
11
12
       
       
       
class UserService {
@Resource
var userRepository: UserRepository = _
def create(user: User) = userRepository.create(user)
def find(name: String) = userRepository.find(name)
def update(user: User) = userRepository.update(user)
def delete(user: User) = userRepository.delete(user)
}

There may be more than one of us UserRepository The implementation of the , such as JPA, JDBC, Hibernate, iBATIS etc. , Then the specific implementation is passed through Spring Injection into UserService in .
Here we use a Mock Let's make this simple Trait, Then simulate Spring Inject ,(Spring According to the configuration and Reflect Automatic instantiation and injection , Here we just simulate its principle , Not used Spring frame ):

       
       
       
1
2
3
4
5
6
7
       
       
       
object Main extends App {
val service = new UserService()
val userRepository = new MockUserRepository()
service.userRepository = userRepository // inject userRepository into userService
service.create(User( "user"))
}

Scala/Cake pattern Implement dependency injection

Similar to the code above , We have three class/trait: UserRepository , MockUserRepository and UserService , among MockUserRepository yes UserRepository The concrete realization of ,
Now we want to put MockUserRepository Injection into UserService . Be careful UserService and UserRepository There are currently no dependencies .

Scala In the cake mode of, we need to declare several Component :

       
       
       
1
2
3
4
       
       
       
trait UserRepositoryComponent {
val userRepository:UserRepository
}
       
       
       
1
2
3
4
       
       
       
trait UserServiceComponent {
this: UserRepositoryComponent =>
val userService: UserService
}

Use here self-type annotation Statement UserServiceComponent need UserRepositoryComponent ( this: UserRepositoryComponent => ). If you need more than one dependency , You can use the following format :

       
       
       
1
       
       
       
this: Foo with Bar with Baz =>

The rest is injected , Generate a ComponentRegistry object :

       
       
       
1
2
3
4
       
       
       
object ComponentRegistry extends UserServiceComponent with UserRepositoryComponent {
override val userRepository: UserRepository = new MockUserRepository
override val userService: UserService = new UserService(userRepository)
}

Very beautiful implementation , If the corresponding dependency is not provided , Or spelling mistakes , When compiling, it can prompt us immediately .
Another advantage is that all objects are val Type of .

So you can go through ComponentRegistry.userService To use the top-level components .

In this way, we can achieve a " clean " Implement different component injection , For example, we want to unit test userService , Its dependence userRepository adopt mock The way to provide :

       
       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
       
       
       
import org.scalatest.mock.MockitoSugar
import org.mockito.Matchers._
import org.mockito.Mockito._
trait TestEnvironment extends UserServiceComponent with UserRepositoryComponent {
val userRepository = MockitoSugar.mock[UserRepository]
val userService = new UserService(userRepository)
}
object Test extends App with TestEnvironment{
val user = User( "user")
userService.create(user)
verify(userRepository, times( 1)).create(any[User])
}

It is very convenient to implement new dependency injection for testing , among userRepository from mock Realization .

This is the realization of cake mode . adopt component trait Abstract interfaces and dependencies , Last in one ComponentRegistry Inject specific implementations .

Reference documents

  1. Scalable Component Abstractions
  2. Real-World Scala: Dependency Injection (DI)
  3. DI in Scala: Cake Pattern pros & cons
  4. Dependency injection vs. Cake pattern
  5. Cake pattern in depth
  6. Cake Pattern in Scala / Self type annotations / Explicitly Typed Self References - explained

版权声明
本文为[Bird's nest]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/10/20211009000611561x.html

随机推荐