vendredi 4 octobre 2019

How to provide alternative implementation of abstract interface in Scala?

Here's the problem I'm facing:

I have an abstract trait Toggles which has an abstract method isToggleEnabled(name: String).

What I want to have is to be able to have production environment trait ProdToggles with concrete implementation of isToggleEnabled and use it in the prod code but be able to provide overriding alternate trait TestToggles with new implementation isToggleEnabled when testing.

The reason is that production environment implementation uses System properties to store toggle names and their states, however in testing I would like to provide different implementation to store and read toggles, so that multiple tests could be run in parallel without affecting each other (as through System properties they would). What I came up with using cake pattern:

trait Toggleable {
  def isToggleEnabled(name: String): Boolean
}

trait ProdToggles extends Toggles with Toggleable {
  override def isToggleEnabled(name: String): Boolean = System.getProperty("name").toBoolean
}

trait TestToggles extends Toggles with Toggleable {
  val cache = scala.collection.mutable.HashMap.empty[String, Boolean]
  override def isToggleEnabled(name: String): Boolean = cache.getOrElse(name, false)
}

trait Toggles {
  this: Toggleable =>

  def isEnabled(name: String): Boolean = {
    isToggleEnabled(name)
  }
}

//—————IN PROD code—————

class Prod() {

  this: Toggles =>

  def doSomething(): Unit ={
    isEnabled("toggle.name")
  }
}

object Prod {
  def apply(): Prod = new Prod with ProdToggles
  def apply(testing: Boolean) = new Prod with TestToggles
}

//——————IN TEST code———————————
class Tests {
  val prod = Prod(true)
  prod.doSomething()
}

However, problem with that:

  1. Breaks encapsulation and Prod instance could be misused as Toggle instance as you could do (new Prod with ProdToggles).{isEnabled, isToggleEnabled, doSomething}
  2. Every Prod class mixin Toggles will need an object with apply constructors to return custom instance for testing and for prod
  3. Cake-pattern is an anti-pattern

Would you have any other approaches to go around this problem? Thanks!

Aucun commentaire:

Enregistrer un commentaire