Шаблон торта Scala и аннотации собственного типа

Я пытаюсь следовать примеру из этого блог. Я понимаю пример, но у меня проблемы с его реализацией.

trait Database {
  // ...
}

trait UserDb {
  this: Database =>
    // ...
}

trait EmailService {
  this: UserDb =>
    // Can only access UserDb methods, cannot touch Database methods
}

В примере упоминается, что полная функциональность базы данных будет скрыта от EmailService — это то, что мне нужно, но я не знаю, как правильно реализовать эти черты.

Вот что я пытался реализовать:

trait Database {
    def find(query: String): String
  }

  trait UserDb {
    this: Database =>
  }

  trait EmailService {
    this: UserDb =>
  }

  trait MongoDatabase extends Database {

  }

  trait MongoUserDb extends UserDb with MongoDatabase{

  }

  class EmailServiceImpl extends EmailService with MongoUserDb {
    override def find(query: String): String = {
      "result"
    }
  }

Мне это кажется странным, потому что черта MongoDatabase не запрашивала реализацию find, и когда я реализовал EmailService, мне было предложено реализовать find, хотя в примере упоминалось, что это будет скрыто от EmailService. Что мне здесь не хватает?

Прочитав ваши комментарии, я пытаюсь реализовать то, что пытаюсь понять, на примере, который ближе к тому, что я на самом деле пытаюсь сделать.

Первый фрагмент не скомпилируется, а второй будет... В конце концов, я хочу иметь разные реализации Repository, в которых я могу переключаться между базами данных, на которые они полагаются, могу ли я закрыть один из приведенных ниже фрагментов?

trait Database {
    def find(s: String): String
  }

  trait Repository {
    this: Database =>
  }

  class UserRepository extends Repository {
    def database = new MongoDB

    class MongoDB extends Database {
      def find(s: String): String = {
        "res"
      }
    }
  }


trait Repository {
    def database: Database

    trait Database {
      def find(s: String): String
    }
  }

  trait UserRepository extends Repository {
    def database = new MongoDB

    class MongoDB extends Database {
      def find(s: String): String = {
        "res"
      }
    }
  }

person Killyz    schedule 12.04.2016    source источник
comment
EmailServiceImpl расширяет MongoUserDb, который расширяет MongoDatabase, который расширяет Database. Где-то в этой иерархии должна быть реализация find (потому что EmailServiceImpl не является абстрактным). Это не имеет ничего общего с аннотациями самостоятельного ввода или шаблоном торта.   -  person Jesper    schedule 12.04.2016
comment
Я понимаю .. Я только изучаю эту тему, и блог не был так ясен (по крайней мере, для меня).   -  person Killyz    schedule 12.04.2016
comment
Вы взглянули на код, который я разместил, я думаю, он мог бы послужить вашей цели.   -  person Som Bhattacharyya    schedule 14.04.2016


Ответы (2)


Как уже упоминалось, MongoUserDB не будет запрашивать реализацию, поскольку это trait. Однако, поскольку EmailServiceImpl extends черта, она должна обеспечить реализацию. То, что вы ищете, можно сделать, добавив еще одну абстракцию. Я делаю это, используя архитектуру service и DAO. Ниже приведен рабочий пример, который вы можете использовать, чтобы убедиться, что он вам подходит.

//All future versions of DAO will extend this
trait AbstractDAO{
  def getRecords:String
  def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
  override def getRecords={"Here are DB records"}
  override def updateRecords(records:String){
    //Actual DB calls and operations
    println("Updated "+records)
  }
}
//Second concrete version
trait concreteDAO1 extends AbstractDAO{
  override def getRecords={"DB Records returned from DAO2"}
  override def updateRecords(records:String){
    //Actual DB calls and operations
    println("Updated via DAO2"+records)
  }
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
  this:AbstractDAO =>

  def updateRecordsViaDAO(record:String)={  
  updateRecords(record) 
  }
  def getRecordsViaDAO={
  getRecords
  }
}

//Test Stub
object DI extends App{
  val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
  wiredObject.updateRecords("RECORD1")
  println(wiredObject.getRecords)

  val wiredObject1 = new service with concreteDAO1
  wiredObject1.updateRecords("RECORD2")
  println(wiredObject1.getRecords)

}

РЕДАКТИРОВАТЬ ---

Вот код, который вы, возможно, захотите реализовать,

    trait Database {
    def find(s: String): String
  }

trait MongoDB extends Database{
  def find(s:String):String = { "Test String" }
}
trait SQLServerDB extends Database{
  def find(s:String):String = { "Test String2" }
}

  trait Repository {
    this: Database =>
  }

  class UserRepository extends Repository with MongoDB{  //  UserRepository is injected with MongoDB here
    find("call MongoDB") //This call will go to the find method in MongoDB trait
  }

  class UserRepository1 extends Repository with SQLServerDB{  //  UserRepository is injected with SQLServerDB here
    find("call SQLServerDB") //This call will go to the find method in SQLServerDB trait
  }
person Som Bhattacharyya    schedule 12.04.2016
comment
Где вы реализуете настоящие CRUD базы данных здесь? - person Killyz; 12.04.2016
comment
Внутри ConcreteDAO и ConcreteDAO1. Вместо методов getRecords и updateRecords вы реализуете методы CRUD. - person Som Bhattacharyya; 13.04.2016

Database скрыт от EnailService, но не от EmailServiceImpl. Последний является подклассом MongoUserDB, очевидно, он имеет к нему доступ. MongoUserDB не "запрашивает" реализацию find, потому что это трейт, а трейты могут иметь абстрактные методы. Вы все равно должны реализовать это там, даже без запроса;)

person Dima    schedule 12.04.2016
comment
понятно.. А как насчет того, как я все реализовал... не могли бы вы что-нибудь изменить? - person Killyz; 12.04.2016
comment
Не знаю, не зная специфики API, в целом это имеет смысл ... MongoUserDb кажется немного избыточным, я бы, наверное, покончил с ним и вместо него просто class EmailServiceImpl extends EmailService with UserDb with MongoDatabase. Кроме этого, ЛГТМ. - person Dima; 12.04.2016
comment
Я согласен... в реальном API я хочу реализовать черты Database, Repository и Service, где Repository использует реализацию Database, а Service - это реализация, с которой вы будете взаимодействовать. - person Killyz; 12.04.2016