Showing posts with label Self. Show all posts
Showing posts with label Self. Show all posts

Sunday, August 16, 2015

Cake Pattern with Scala, A Simple Example


Cake Pattern with Scala, A Simple Example



Cake Pattern is a software design pattern used for dependency injection. In Scala it can be implemented with 'Self' keyword. 'Self' is used to create 'required' relationship. Here we are going to explain Cake pattern with a simple example.

Scenario
Lets say we need to create a software for a shop. Therefore we create a shop class. To do the businesses in the shop, calculations (adding, deducting and etc) are needed to be done. Calculations are normally different from daily activities in the shop, so it needs to be separated. Therefore we need a calculator. In other words the shop needs the calculator. Calculator should be injected as a dependency to the shop.

Dependency Injection
What are the basic ways of doing the dependency injections? There are many ways of doing this. Few methods are listed down below.


  • Using a constructor, we pass the dependencies
                   E.g.: Shop(calc:Calculator)
  • Using the self key word in Scala (the earlier version of the cake pattern)
  • Cake pattern
  • etc...



Using the self keyword 

First lets start with the 'self' key word. Understanding the 'Self' type is useful, before going to Cake Pattern examples.


Writing the super trait to abstract things; 

trait Calculator {
  def add(a: Int, b: Int): Int
}

Provide multiple implementations E.g.: 'CalculatorImp' and 'CalculatorDifferentImp'

trait CalculatorImp extends Calculator { 
 def add(a: Int, b: Int): Int = { a + b } 
}

CalculatorDifferentImp can be a mocked implementation which can be used for/in unit testing.

trait CalculatorDifferentImp extends Calculator {
 val dummyVal = 5000 
 override def add(a: Int, b: Int): Int = { 
   dummyVal //just a dummy data. Probably for a testing purpose 
 } 
}
  • Applying with 'Self' and 'with'
class Shop {
  self: Calculator =>

  def printTotal(a1: Int, a2: Int): Unit = {
    println("Total = " + add(a1, a2))
  }
}
//--------writing the main and wiring----------------------

object Main { 

   def main(args: Array[String]) { 
        val worker = new Shop with CalculatorImp 
        worker.printTotal(10,20) 
        val worker2 = new Shop with CalculatorDifferentImp 
        worker2.printTotal(10,20) 
  } 
}
//----------Output will be;----------
Total = 30
Total = 5000



Have we done dependency injection here? Yes. Different implementations of Calculator can be injected and can work with the functionalities given in Shop.


Additional Note

But same thing can be achieved with 'extends' only, instead of using 'self'.


The only difference is that Shop class needs to be changed to a trait from a class. 





trait Shop extends Calculator {
  //self: Calculator => //Note that Shop is not a trait anymore
  def printTotal(a1: Int, a2: Int): Unit = {
      println("Total = " + add(a1, a2))
  }


object Main {
   def main(args: Array[String]) {
      val worker = new Shop with CalculatorImp
      worker.printTotal(10,20)
      val worker2 = new Shop with CalculatorDifferentImp
      worker2.printTotal(10,20)
   }
}




What is wrong here? Extends is not used for such purposes. In simple terms the Shop is not a Calculator. Therefore even the code works well, it should not be written as mentioned. 'Self' is to create 'requires' relationship but 'extends' is to create 'is-a' relationship with inheritance.

So why Cake Pattern?


As you have seen so far, the Calculator was successfully injected as a dependency to Shop. But there is something wrong with the example we have discussed up to now. In 'Shop' class, we have called the 'add' method.

   .... 
   println("Total = " + add(a1, a2))
   .... 

For a developer who is not familiar with the code might think how this add method appears here. As a solution, we can change the code as given below.



class Shop {
  calculator: Calculator =>

  def printTotal(a1: Int, a2: Int): Unit = {
    println("Total = " + 
        calculator.add(a1, a2)) 
    } 
}


Now it looks better. But still there are many issues that can occur if we try to expand the implementation with new features.
Lets say the shop needs an inventory. The developers have to create the Inventory class. It is a dependency of the shop. So lets start the implementation.

Now both Calculator and Inventory are dependencies to Shop.

First write the Inventory trait. Also extend it and write its implementation (InventoryImpl). (Probably you will have different implementations for unit testing)

trait Inventory {
  def addItemNumber(itemNumber: Long)
}
trait InventoryImpl extends Inventory{
  def addItemNumber(itemNumber: Long): Unit ={
    //implementation
  }
}


Injecting the Inventory to Shop is done in the below given code. But inside 'Shop' it is difficult to find out how it has accessed the 2 methods 'add' and 'addItemNumber'

trait Shop {
  self: Calculator with Inventory =>
def printTotal(a1: Int, a2: Int): Unit = {

    println("Total = " + add(a1, a2))
  }

  def addItem(item: Long): Unit = {

    addItemNumber(item)
  }
}

object Main {

  def main(args: Array[String]) {
    //You can see that InventoryImpl is injected using with key word
    val shop = new Shop with CalculatorImp with InventoryImpl
    shop.printTotal(10, 20)
    shop.addItem(10012)
  }
}

It might look nice since there are only 2 dependencies at the moment. What if there are about 20 dependencies for the Shop. It would be very difficult to find the methods which are called from these dependencies.

Cake Pattern

Cake pattern can be considered as an extension of what we have discussed so far. Instead of injecting the dependency directly using the 'self', we add a middle layer. Lets call it the component layer for the moment.
Component layer is used to wrap the dependency trait. In Cake Pattern we inject the component instead of injecting the dependency.

Lets start writing the component layer. Create the CalculatorComponent trait. Inside add the access point to the Calculator as 'def calculator'. To add implementations you can extend the CalculatorComponent as given below;

trait CalculatorComponent {
  def calculator : Calculator
}

trait DefaultCalculatorComponent extends CalculatorComponent {

  override val calculator = new CalculatorImp
}


Please note that CalculatorImp is now a class and not a trait (it can be a trait as well. For this example it would be easier if it was an object).

Now the entire code looks as given below (Note that to make it easier to understand we have removed the Inventory dependency);


trait Shop {
    println("Total = " + calculator.add(a1, a2))


  self: CalculatorComponent =>
  def printTotal(a1: Int, a2: Int): Unit = {

  }
}
trait CalculatorComponent {
  def calculator : Calculator
}
trait DefaultCalculatorComponent extends CalculatorComponent {
  override val calculator = CalculatorImp
}
trait Calculator {
  def add(a: Int, b: Int): Int
}
object CalculatorImp extends Calculator {
  def add(a: Int, b: Int): Int = {
    a + b
  }
}
trait CalculatorDifferentImp extends Calculator {
  val dummyVal = 5000
  override def add(a: Int, b: Int): Int = {
    dummyVal //just a dummy data. Probably for a testing purpose
  }
}
object Main {
  def main(args: Array[String]) {
    val worker = new Shop with DefaultCalculatorComponent 
    worker.printTotal(10, 20)
  }

}

Note that the 'add' method is now called as 'calculator.add(a1,a2)'. Therefore dependencies are clearly managed and code is clearer and more readable with Cake Pattern.