Upload
adam-warski
View
4.862
Download
0
Embed Size (px)
DESCRIPTION
Using a DI framework/container may seem obvious. But when was the last time you considered *why* do you really need one? After all, "dependency injection" is just a fancy name for passing arguments to a constructor. In the talk we'll walk through some of the features of DI containers and see if we can replace them with pure Scala code. We'll start with "manual" DI, followed with using MacWire to generate the wiring code for us. Then we'll proceed to a no-framework scopes implementation (e. g. request or session), which are very useful in web applications. We will also discuss possibilities of adding interceptors using macros. And finally, we'll see how to use traits to create and compose modules (similar to the module concept known from Guice), which can be viewed as a simplified cake pattern. As MacWire heavily uses macros, as a bonus, I'll explain how Scala Macros work and when they can be useful.
Citation preview
11/12/2013 BuildStuff 2013 @adamwarskiAdam Warski
BuildStuff 2013
The no-framework Scala Dependency Injection
framework
@adamwarski11/12/2013 BuildStuff 2013
You don’t need anything special to do Dependency Injection
@adamwarski11/12/2013 BuildStuff 2013
We often over-complicate
@adamwarski11/12/2013 BuildStuff 2013
Who Am I?
❖ Day: coding @ SoftwareMill!
❖ Afternoon: playgrounds, Duplos, etc.!
❖ Evenings: blogging, open-source!
❖ Original author of Hibernate Envers!
❖ ElasticMQ, Veripacks, MacWire!
❖ http://www.warski.org
@adamwarski11/12/2013 BuildStuff 2013
What is Dependency Injection?
@adamwarski11/12/2013 BuildStuff 2013
What is DI?
class PresentPackager {!
! def wrap() {!
! ! new RibbonSelector().selectRandom()!
! ! …!
} !
}
@adamwarski11/12/2013 BuildStuff 2013
What is DI?
class PresentPackager(rs: RibbonSelector) {!
! def wrap() {!
! ! rs.selectRandom()!
! ! …!
} !
}
@adamwarski11/12/2013 BuildStuff 2013
Yes, DI is just using parameters
@adamwarski11/12/2013 BuildStuff 2013
Why?❖ Restrict the knowledge of the class
class PresentPackager ! (rs: RibbonSelector) { ! def wrap() { ! ! rs.selectRandom() ! ! … ! } }
class PresentPackager { ! def wrap() { ! ! new RibbonSelector() ! ! ! .selectRandom() ! ! … ! } }
@adamwarski11/12/2013 BuildStuff 2013
But still …
❖ We need to have the news somewhere
@adamwarski11/12/2013 BuildStuff 2013
Let’s create a DI container! a.k.a. framework
@adamwarski11/12/2013 BuildStuff 2013
DI in Java
❖ Many frameworks!
❖ Configuration via:!
❖ XML!
❖ annotations!
❖ Java
@adamwarski11/12/2013 BuildStuff 2013
What’s wrong with that?
❖ Do I really need a DI framework?
@adamwarski11/12/2013 BuildStuff 2013
Let’s go back …
❖ … and just use our host language!
❖ in this case, Scala!
❖ mapping DI framework concepts to native language constructs
@adamwarski11/12/2013 BuildStuff 2013
Manual DI!!
object PresentWrapper extends App {!
! val ribbonSelector = ! ! ! ! new RibbonSelector()!
! val wrappingPaperFeeder = ! ! ! ! new WrappingPaperFeeder()!
! val presentPackager = ! ! ! ! new PresentPackager( ! ! ! ! ! ribbonSelector, ! ! ! ! ! wrappingPaperFeeder)!
}
@adamwarski11/12/2013 BuildStuff 2013
Manual DI!!
object PresentWrapper extends App {!
! lazy val ribbonSelector = ! ! ! ! new RibbonSelector()!
! lazy val wrappingPaperFeeder = ! ! ! ! new WrappingPaperFeeder()!
! lazy val presentPackager = ! ! ! ! new PresentPackager( ! ! ! ! ! ribbonSelector, ! ! ! ! ! wrappingPaperFeeder)!
}
@adamwarski11/12/2013 BuildStuff 2013
MacWireimport com.softwaremill.macwire.MacwireMacros._!
object PresentWrapper extends App {!
! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!
! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!
! lazy val presentPackager = ! ! ! ! wire[PresentPackager]
}
@adamwarski11/12/2013 BuildStuff 2013
Side-note: Scala Macros
@adamwarski11/12/2013 BuildStuff 2013
Side-note: Scala Macros
❖ Scala code executed at compile time!
❖ Operate on trees!
❖ Can inspect the environment, generate code!
❖ the code is type-checked
@adamwarski11/12/2013 BuildStuff 2013
Side-note: Scala Macros❖ E.g. debug macro!
def debug(params: Any*) = macro debug_impl!
def debug_impl ! ! ! (c: Context)! ! ! (params: c.Expr[Any]*): c.Expr[Unit]!
!
debug(presentCount) ⟹!
! println(“presentCount = “ + presentCount)
@adamwarski11/12/2013 BuildStuff 2013
Side-note: Scala Macros
❖ Debug macro implementation!
import c.universe._!
val paramRep = show(param.tree) val paramRepTree = Literal(Constant(paramRep)) val paramRepExpr = c.Expr[String](paramRepTree)!
reify { println( ! paramRepExpr.splice + ! " = " + ! param.splice) }
@adamwarski11/12/2013 BuildStuff 2013
Side-note: Scala Macros
❖ MacWire!
def wire[T] = macro wire_impl[T]!
def wire_impl ! ! ! [T: c.WeakTypeTag]! ! ! (c: Context): c.Expr[T]
@adamwarski11/12/2013 BuildStuff 2013
MacWireimport com.softwaremill.macwire.MacwireMacros._!
object PresentWrapper extends App {!
! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!
! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!
! lazy val presentPackager = ! ! ! ! wire[PresentPackager]}
@adamwarski11/12/2013 BuildStuff 2013
Scopes
❖ How long will an object (instance) live?
@adamwarski11/12/2013 BuildStuff 2013
Singleton & dependent
object NorthPole extends App {!
! // Singleton!
lazy val santaClaus = wire[SantaClaus]!
!
! // Dependent!
def gnome = wire[Gnome]!
}
@adamwarski11/12/2013 BuildStuff 2013
Arbitrary scopestrait WebFrontEnd { ! lazy val loggedInUser = ! ! session(new LoggedInUser) ! def session: Scope }!
!
trait Scope { ! def apply(factory: => T): T }
@adamwarski11/12/2013 BuildStuff 2013
Arbitrary scopes
object MyApp extends WebFrontEnd { ! val session: Scope = ! ! ! ! new ThreadLocalScope()!
! val filter = new ScopeFilter(session) ! // bootstrap the web server ! // using the filter}
@adamwarski11/12/2013 BuildStuff 2013
Arbitrary scopes
class ScopeFilter(sessionScope: ThreadLocalScope) ! ! ! extends Filter {!
! def doFilter(request: ServletRequest) { ! ! sessionScope ! ! ! .withStorage(request.getSession()) {!
! ! ! request.proceed()!
! ! } ! } }
@adamwarski11/12/2013 BuildStuff 2013
Modules
❖ Pre-wired!
❖ Composable!
❖ Dependencies!
❖ Module per package?!
❖ Veripacks :)
@adamwarski11/12/2013 BuildStuff 2013
Modules
❖ Module: trait!
❖ Pre-wired: new, MacWire!
❖ Composable: extends/with!
❖ Dependencies: extends/with / abstract members
@adamwarski11/12/2013 BuildStuff 2013
Modules
trait PresentWrapper {!
! lazy val ribbonSelector = ! ! ! ! wire[RibbonSelector]!
! lazy val wrappingPaperFeeder = ! ! ! ! wire[WrappingPaperFeeder]!
lazy val presentPackager = ! ! ! ! wire[PresentPackager]!
}
@adamwarski11/12/2013 BuildStuff 2013
Modules
trait PresentFactory extends PresentWrapper {!
! lazy val teddyBearProvider = ! ! ! ! wire[TeddyBearProvider]!
! lazy val toyTrainProvider = ! ! ! ! wire[ToyTrainProvider]!
lazy val presentAssembly = ! ! ! ! wire[PresentAssembly]!
}
@adamwarski11/12/2013 BuildStuff 2013
Modules
trait HomeOfSanta {!
! lazy val santaClaus = wire[SantaClaus]!
! lazy val rudolf = wire[Rudolf]!
! lazy val fireplace = wire[Fireplace]!
!
def presentAssembly: PresentAssembly!
}
@adamwarski11/12/2013 BuildStuff 2013
Modulestrait PresentWrapper { … } trait PresentFactory extends PresentWrapper { } trait HomeOfSanta { … }!
!
object NorthPole ! extends PresentWrapper ! with PresentFactory ! with HomeOfSanta {!
! santaClaus.deliver()!
}
@adamwarski11/12/2013 BuildStuff 2013
Testing Santa’s Homeclass HomeOfSantaTest extends FlatSpec {!
! it should “deliver presents” in {!
! ! val mockPresentAssembly = …!
! ! new HomeOfSanta {!
!! lazy val presentAssembly = ! ! ! ! mockPresentAssembly }!
! ! …!
! }!
}
@adamwarski11/12/2013 BuildStuff 2013
Cake Patterntrait PresentPackagerModule {!
! class PresentPackager { ! ! def wrap() { ! ! ! ribbonSelector.selectRandom() ! ! ! … ! ! } ! }
! lazy val presentPackager = new PresentPackager() ! def ribbonSelector: RibbonSelector }
@adamwarski11/12/2013 BuildStuff 2013
Cake Pattern
val cake = new PresentPackagerModule ! with RibbonSelectorModule ! with WrappingPaperFeederModule ! with TeddyBearProviderModule ! with ToyTrainProviderModule ! with PresentAssemblyModule ! with … { }
@adamwarski11/12/2013 BuildStuff 2013
Other features
❖ Interceptors!
!
trait Chimney {!
! lazy val presentTransferer = ! ! transactional(wire[PresentTransferer])!
! def transactional: Interceptor!
}
@adamwarski11/12/2013 BuildStuff 2013
Other features
❖ Factories!
❖ a dedicated object or …!
!
trait PresentBoxer {!
! def box(size: Size) = wire[Box]!
}
@adamwarski11/12/2013 BuildStuff 2013
Other features
❖ Instance maps!
❖ for integrating e.g. with Play!
❖ Factories!
❖ In-method wiring!
❖ More coming, someday :)
@adamwarski11/12/2013 BuildStuff 2013
Summing up
❖ Reconsider using a framework!
❖ Native Scala gives a lot of power!
❖ use it!
❖ wisely!
❖ More flexibility (less constraints)
@adamwarski11/12/2013 BuildStuff 2013
Links
❖ http://www.warski.org!
❖ https://github.com/adamw/macwire!
❖ http://springsource.com/
@adamwarski11/12/2013 BuildStuff 2013
Thanks!
❖ Questions?!
❖ Stickers ->!