Cake Pattern in Scala

2016/04/26

Last week I started looking into Scala, it’s one of those things that has been on my to-do list for a very long time and but never got to it until now. Looking back, I should have bumped it up the list a long time ago.

A couple of interesting patterns I’ve came across was the Cake and the Magnet pattern. I usually find it useful to relate new things to my old understanding in other technology stack, and being primarily a Java developer, here is a my short explanation with Java.

Cake Pattern Explained

This ‘Cake Pattern’ is a random name (IMO) of what Java developer normally called ‘best practice dependency injection (DI)’ with Spring. Many of you here have probably been doing this for a very long time already in the Java/Spring world. Basically it means ‘one should only inject via interface, not concrete class’.

Dependency injection

Imagine if I have a service that needs to do some data operation, normally we’d create a ‘Service’ class with a ‘DAO’ member – where ‘DAO’ is an interface. At runtime, we’d inject what DAO implementation we want, eg. SqlDao or CloudantDao for example.

MyService –(uses)–> GenericDAO interface**

Here in Scala a ‘trait’ act like a plain old Java interface or contain implementation (like Java 8 ‘default’ method on interface), so we use it to declare both interface and implementation:

SqlDao –(implements)–> GenericDAO interface

CloudantDao –(implements)–> GenericDAO interface

Then at when you need to run the code, just inject whichever implementation you want into the service

MyService –(uses)–> GenericDAO interface (with SqlDao implementation injected) 

or

MyService –(uses)–> GenericDAO interface (with CloudantDao implementation injected) 

Scala Cake Pattern Example

(github)

Here is a little demo program to show this DI concept.

package com.example

object MyCake extends App {

  // This is an 'interface'
  trait GenericDao {
    def Imp: String
  }

  // This is like implementing interface with default in Java8 (An implementation)
  trait SqlDao extends GenericDao { 
    def Imp: String = "SQL Implementation"
  }

  // This is like implementing interface with default in Java8 (An implementation)
  trait CloudantDao extends GenericDao {

    def Imp: String = "Cloudant Implementation"
  }

  trait MyServiceTrait {
    // This relies on 'interface' instead of 'implementation' - it just normal Java/Spring best practice anyway.
    dao: GenericDao => // Syntax for trait 'self type annotation', we really should just call it 'extending an interface'
    println("My implementation is: " + dao.Imp)
  }

  val svc = new MyServiceTrait with CloudantDao  // DI with Cloudant Impl
  println("svc using Cloudant DI returns: " + svc.Imp)

  val svc2 = new MyServiceTrait with SqlDao  // DI with SQL Impl
  println("svc2 using SQL DI returns: " + svc2.Imp)

}

Update: But wait – self type annotation is more like ‘inheritance’ than ‘composition’

After having a chat with a colleague – notice in the example above there is:

dao: GenericDao => 

In Java/Spring, DI is usually composition – but here we are doing DI via traits and the self type annotation is more like inheriting. Normally, in most example I saw, it’s named ‘this’ instead of ‘dao’ – I guess that make more sense if we are inheriting (injecting) multiple traits. See the updated example below multiple traits:

package com.example

object MyCake extends App {

  // shared

  trait AnotherTrait {
    // Some method
  }

  trait GenericDao { // This is an 'interface'
    def Imp: String
  }

  trait SqlDao extends GenericDao { // This is like implementing interface with default in Java8 (An implementation)
    def Imp: String = "SQL Implementation"
  }

  trait CloudantDao extends GenericDao { // This is like implementing interface with default in Java8 (An implementation)
    def Imp: String = "Cloudant Implementation"
  }

  trait MyServiceTrait {
    // This relies on 'interface' instead of 'implementation' - it just normal Java/Spring best practice anyway.
    this: GenericDao with AnotherTrait => // Syntax for trait 'self type annotation', we really should just call it 'extending an interface'
    println("My implementation is: " + this.Imp)
  }


  // service customisation (inject stuff here)

  val svc = new MyServiceTrait with CloudantDao with AnotherTrait
  println("svc using Cloudant DI returns: " + svc.Imp)

  val svc2 = new MyServiceTrait with SqlDao with AnotherTrait
  println("svc2 using SQL DI returns: " + svc2.Imp)

}

Post Directory