Scala事件

Scala事件,scala,events,Scala,Events,我有一个CategoryRepository类,它实现了几个方法,可以将一个类别保存到数据库中。 我还有一个包含类别列表的对象产品。 我想做的是触发Product对象将侦听的事件,并使用其类别的新数据更新产品本身 在C语言中,我知道我可以使用委托,但我不知道在Scala中是否可以这样做 我不想让CategoryRepository类知道类Product,所以我不会调用Product中的某些方法来通过CategoryRepository更新它 我的分类报告类: trait CategoryRepo

我有一个CategoryRepository类,它实现了几个方法,可以将一个类别保存到数据库中。 我还有一个包含类别列表的对象产品。 我想做的是触发Product对象将侦听的事件,并使用其类别的新数据更新产品本身

在C语言中,我知道我可以使用委托,但我不知道在Scala中是否可以这样做

我不想让CategoryRepository类知道类Product,所以我不会调用Product中的某些方法来通过CategoryRepository更新它

我的分类报告类:

trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {

  val categoryRepository = new categoryRepositoryImpl

  class CategoryRepositoryImpl extends CategoryRepository {

    val dbRepository = CategoryDbRepository

    def updateAttribute(id:String, request:UpdateCategoryItem): String = { 
      val cat = dbRepository.get(id)
      cat.update(request)
      dbRepository.save(cat)
    }
  }
}
产品存储库看起来与此类别的存储库相同。 现在,我想在dbRepository.savecat之后添加一行,该行将触发一个事件,该事件将调用ProductRepository中的updateProduct函数

请给出一个实现示例。
谢谢。

我想这或多或少就是C语言中事件和委托的实现方式。 当您定义这样的事件时,如果您想将多个参数传递给事件侦听器,那么必须使用tuples Event[Any,EventArgs]

class Event[Arg] {

  type L = Arg => Unit

  private val listeners = scala.collection.mutable.ListBuffer.empty[L]

  def +=(listener: L) {
    listeners.append(listener)
  }

  def apply(arg: Arg) {
    listeners.foreach(_(arg))
  }
}

class CategoryRepository {

  val event = new Event[String]

  def fireEvent(data: String) {
    event(data)
  }
}

object Product {

  def update(data: String) {
    println(s"updating: $data")
  }
}

object Main extends App {

  val repo = new CategoryRepository()
  repo.event += Product.update
  repo.fireEvent("new data")
}

基于事件的更新通道的非基本实现

我只注意概括到最基本的部分,以便为将来的进化和代码重用提供提示

基础设施 我们推出了一个更新频道

//Listens for updates to A's and notifies interested listeners
class UpdateChannel[A] {

  //simplified register
  var listenMap: Map[A, Listening[A]] = Map()

  //update call
  def apply(a: A): Unit = listenMap.get(a).foreach(_.event(a))

  //update call
  def registerFor(a: value, listen: Listening[A]) = listenMap += (a, listen)

}
还有一个对更正更新感兴趣的普通侦听器

//Listens to changes for type A
trait Listening[A] {

  def event(upd: A): Unit

}
应用 现在我们调整Repo组件来注入通道

trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {

  val categoryRepository = new categoryRepositoryImpl

  /************** NEW CODE HERE **************
   * define a channel to send category updates
   *******************************************/
  def updateChannel: UpdateChannel[Category]

  class CategoryRepositoryImpl extends CategoryRepository {

    val dbRepository = CategoryDbRepository

    def updateAttribute(id:String, request:UpdateCategoryItem): String = { 
      val cat = dbRepository.get(id)
      cat.update(request)
      dbRepository.save(cat)
      //send the update to the channel
      updateChannel(cat)  //***************** << AND HERE
    }
  }
}
最后,我们把所有的材料放在一起

//put the pieces together
def wireup() = {

  //the channel  
  val catChan: UpdateChannel[Category] = new UpdateChannel[Category]

  //the Repository component wired to the channel
  val catRep = new CategoryRepositoryComponentImpl {
    val updateChannel = catChan
  }

  //a nice cat
  val myCat: Category = ???

  //a nice prod with her nice cat
  val p: Product = new Product(myCat)

  //prod wants to know what happens to her cat
  catChan.registerFor(myCat, p)

}
评论 通过使用细化类型,我们可以使产品独立于整个框架

val product = new Product(myCat) with Listening[Category] {
  def event(cat: Category) = ??? //strut your stuff here
}
另一种解决方案是避免所有的连接,只需在RepositoryComponent中注册更新闭包列表

}

而产品只需添加自己的更新功能

    catRep.categoryUpdates +:= (cat) => p.event(cat)

在您的示例中,您使用Main实例化CategoryRepository,但在我的示例中,它发生在一个trait中,这意味着trait将通过产品的更新方法,这意味着CategoryRepository的trait知道产品。这也是我不想要的。你能用事件来代替吗?也许你应该展示你的类是如何构造的,以及你想在哪里添加事件监听器。或者你将如何处理代表。嗨,你能分享一个用C语言处理的例子吗?或者使用伪代码。为了弄清楚你在问什么,我不知道C语言的确切语法,但我知道你可以使用delegate并使用+=操作符添加一个事件监听器。我只是不知道Scala是否有类似的东西……不要重新定义Product,它是Scala类型系统中的一个关键内置特性—所有元组和所有case类都派生自Product。我并不真正称之为Product。这只是为了解释我的问题。
trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {

  val categoryRepository = new categoryRepositoryImpl

  //public listeners, they should be encapsulated
  var categoryUpdates: Seq[Category => Unit]

  [...]

  def updateAttribute(id:String, request:UpdateCategoryItem): String = { 
    val cat = dbRepository.get(id)
    cat.update(request)
    dbRepository.save(cat)
    //send the update to the channel
    categoryUpdates.foreach(_.apply(cat))
  }
}
    catRep.categoryUpdates +:= (cat) => p.event(cat)