原文: http://colobu.com/2015/07/28/Scala-Cake-pattern-and-Dependency-Injection/
如果你是一个Java开发者,熟悉 依赖注入 模式, 深度依赖Spring框架的话,在使用Scala做开发时,会遇到一个问题,在Scala世界里,如何实现类似Spring框架的依赖注入呢?
尽管函数式编程的信徒认为他们不需要DI框架,高阶(high-order)函数足够了。但是对于同时支持面向对象的编程和函数式编程的Scala来说,依赖注入是很好的实现应用的一种设计模式。
蛋糕模式(Cake pattern)是Scala实现依赖注入的方式之一。
蛋糕模式是 Scala 之父 Martin Odersky 在其论文 Scalable Component Abstractions 中首先提到。
什么是蛋糕模式呢? 一个非正式的但是很形象的解释是:
- 蛋糕有很多风味, 你可以根据你的需要增加相应的风味。依赖注入就是增加调料。
- 蛋糕有很多层 (layer)。如果你想要一个更大的蛋糕你就可以增加更多的层。
我们先以Spring常用的User数据库读取的实现为例,看看Scala 风格的依赖注入(蛋糕模式)是如何实现的。
Java/Spring风格的依赖注入实现
一个传统的Spring实现是将程序划分为"Repository"层(DAO layer) 和Service层。
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)
}
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)
}
我们可能有多个UserRepository的实现, 比如JPA, JDBC, Hibernate, iBATIS 等,然后将具体的实现通过Spring注入到 UserService中。这里我们用一个Mock来简单这些这个Trait, 然后模拟Spring注入,(Spring会根据配置和Reflect自动实现实例化和注入,这里我们只是模拟其原理,并没有使用Spring框架):
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实现依赖注入
和上面的代码类似,我们有三个class/trait:UserRepository,MockUserRepository和UserService, 其中MockUserRepository是UserRepository的具体实现,
现在我们想把MockUserRepository注入到UserService。注意UserService和UserRepository目前没有任何依赖关系。
Scala的蛋糕模式中我们需要声明几个Component:
trait UserRepositoryComponent {
val userRepository:UserRepository
}
和
trait UserServiceComponent {
this : UserRepositoryComponent =>
val userService: UserService
}
这里使用 self-type annotation 声明UserServiceComponent需要UserRepositoryComponent(this: UserRepositoryComponent =>)。 如果需要多个依赖,可以使用下面的格式:
this : Foo with Bar with Baz =>
剩下的就是注入了,生成一个ComponentRegistry对象:
object ComponentRegistry extends UserServiceComponent with UserRepositoryComponent {
override val userRepository: UserRepository = new MockUserRepository
override val userService: UserService = new UserService(userRepository)
}
挺漂亮的实现, 如果相应的依赖没有提供,或者拼写错误等,编译时能立刻提示我们。
这还有一个好处就是所有的对象都是val类型的。
这样你就可以通过ComponentRegistry.userService来使用顶层的组件了。
这样我们可以实现一种"干净"的方式实现不同的组件注入,比如我们想单元测试userService,其中它的依赖userRepository通过mock的方式提供:
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])
}
可以很方便的为测试实现新的依赖注入,其中userRepository由mock实现。
这就是蛋糕模式的实现。通过 component trait 抽象接口和依赖,最后在一个ComponentRegistry注入各个具体的实现。
相关推荐
scala编程 33章 中文pdf Scala编程实战 目录 第1章字符串. 11 第2章数值39 第3章控制结构.60 第4章类和属性.103 第5章方法147 第6章对象170 第7章包和导入.190 第8章特质200 第9章函数式编程214 第10 章集合242 第...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
Scala编程是一种多范式、函数式和面向对象的编程语言,设计目的是为了提高开发者的生产力,同时保持代码的可维护性和高性能。它是由Martin Odersky在2003年设计并实现的,融合了Java虚拟机(JVM)的优势,并引入了...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
【课程大纲】第1讲-Spark的前世今生第2讲-课程介绍、特色与价值第3讲-Scala编程:基础语法第4讲-Scala编程:条件控制与循环第5讲-Scala编程:函数入门第6讲-Scala编程:函数入门之默认参数和带名参数第7讲-Scala编程:...
《Scala编程(第3版)》面向具有一定编程经验的开发者,目标是让读者能够全面了解和掌握Scala编程语言的核心特性,并能够深入理解Scala这门语言在设计取舍背后的动因。即便是不直接使用Scala或从事一线编程工作的...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
《Scala编程(第3版)》面向具有一定编程经验的开发者,目标是让读者能够全面了解和掌握Scala编程语言的核心特性,并能够深入理解Scala这门语言在设计取舍背后的动因。即便是不直接使用Scala或从事一线编程工作的...
Scala编程指南第三版是针对大数据处理和分布式计算领域的一款重要参考书籍,特别是在Spark生态系统中,Scala作为基础语言,起着至关重要的作用。本指南详细介绍了Scala语言的核心概念、语法特性和高级特性,旨在帮助...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
共13页第15讲-Scala编程详解:面向对象编程之Trait 共14页第16讲-Scala编程详解:函数式编程 共14页第17讲-Scala编程详解:函数式编程之集合操作 共9页第18讲-Scala编程详解:模式匹配 共11页第19讲-Scala编程详解:...
Scala是一种多范式的编程语言,它融合了面向对象和函数式编程的概念,旨在提供一种统一、高效的编程模型。Scala在Java虚拟机(JVM)上运行,并且可以无缝地与Java代码互操作,这得益于其与Java的兼容性。这份"Scala...
Scala的设计目标是融合面向对象编程和函数式编程的最佳实践,提供一种既能够支持传统的对象模型,又可以进行高阶函数编程的语言。它的名字“Scala”恰好体现了这一核心理念,即“可伸缩性”,意味着它可以适应从小...