简体   繁体   中英

How to avoid duplications of mixing of an implementation with the cake pattern

In all articles related to Cake patter that I found on the Internet I see a single level dependencies and it's clear to me.

But when I started using it I faced an issue that I can not use a service only in a high level class and I need to mix it in multiple places.

For example, if I have a service and this service works with a set of other services and each service in this set uses a database, I tried do not give a direct access to the database from this set of low level services. I made all database queries only in the high level service. But It is difficult in some cases.

May be the question will be more clear with the example:

trait DatabaseServiceComponent{
      val databaseService: DatabaseService
      trait DatabaseService{
        def getSomeData(id: Int, tableName: String): List[String]
        def getFriends(id: Int): List[Int]

  }
}

trait DatabaseServiceComponentImpl extends DatabaseServiceComponent{
  val databaseService: DatabaseService = new DatabaseServiceImpl
  class  DatabaseServiceImpl extends DatabaseService{
    def getSomeData(id: Int,  tableName: String): List[String] = ???
    def getFriends(id: Int): List[Int] = ???
  }
}

trait Scoring { this: DatabaseServiceComponent =>
  def importantValues: Set[String]
  val tableName: String
  def getScore(id: Int): Double = databaseService.getSomeData(id, tableName).count(importantValues)
}

class Scoring1 extends Scoring{this: DatabaseServiceComponent =>
  val tableName: String = "s1"
  override def importantValues: Set[String] = Set("a", "b")
}

class Scoring2 extends Scoring{this: DatabaseServiceComponent =>
  val tableName: String = "s2"
  override def importantValues: Set[String] = Set("foo", "bar")
}

class Scoring3 extends Scoring{this: DatabaseServiceComponent =>
  val tableName: String = "s3"
  override def importantValues: Set[String] = Set("1", "2")
}

// How to implement this correctly?
trait Scoring2FriendsAverage {this: DatabaseServiceComponent =>
  val scoring2: Scoring2
  def getScore(id: Int):Double ={
    val scores = databaseService.getFriends(id).map(scoring2.getScore)
    scores.size / scores.sum
  }
}


object FriendsScoringProcessor{

  val scoring2Friends = new Scoring2FriendsAverage with DatabaseServiceComponentImpl{
    val scoring2 = new Scoring2 with DatabaseServiceComponentImpl // I don't like that I have to mix the implementation of a service again
  }

  def printScores(id: Int): Unit = {
    val score = scoring2Friends.getScore(id)
    println(score)
  }

}

I have a set of Scorings and each of them uses a database. I have a FriendsScoring which uses one of scoring which uses the database. I want to be able to mix database implementation only to the FriendsScoring and do not duplicate it in lower level services.

I see one good (may be) solution is to provide an implementation via an implicit constructor argument to low level service.

It looks like mixing levels of cake-pattern components and services.

If we use Scoring at a service layer, then it shouldn't be present at cake-pattern level.

You might want to split Scoring into two nested traits on each level like you did for Database:

trait ScoringComponent {this: DatabaseServiceComponent =>

  trait ScoringService {
    def getScore(id: Int): Double = 
      databaseService.getSomeData(id, tableName).
        count(importantValues)
  }
}

Then you'll be able to use ScoringService after mixing the required dependencies.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM