3

I recently started learning Scala and started a little project to create a simple roguelike game. However, I'm stuck at trying to implement the observer pattern. This answer touches the subject, but I can't figure out how to make it work. Below is the code from the answer linked above. I'm mostly puzzled by the "this: S =>" part of the code, I suppose I should have some kind of a function there, but I'm not sure. I would like to have it return a tuple from the class that will extend the Subject trait.

trait Observer[S] {
 def receiveUpdate(subject: S);
}

trait Subject[S] {
 this: S =>
 private var observers: List[Observer[S]] = Nil
 def addObserver(observer: Observer[S]) = observers = observer :: observers

 def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}
Community
  • 1
  • 1
Zavior
  • 6,412
  • 2
  • 29
  • 38

4 Answers4

10

See Steve's answer about the self type and for another code example.

Here is some sample code using an observer. The ObservedAccount is the Subject that is observed by an AccountReporter observer.

trait Observer[S] {
    def receiveUpdate(subject: S);
}

trait Subject[S] { 
    this: S =>
    private var observers: List[Observer[S]] = Nil
    def addObserver(observer: Observer[S]) = observers = observer :: observers

    def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

class Account(initialBalance: Double) {
    private var currentBalance = initialBalance
    def balance = currentBalance
    def deposit(amount: Double)  = currentBalance += amount
    def withdraw(amount: Double) = currentBalance -= amount
}

class ObservedAccount(initialBalance: Double) extends Account(initialBalance) with Subject[Account] {
    override def deposit(amount: Double) = {
        super.deposit(amount)
        notifyObservers()
    }
    override def withdraw(amount: Double) = {
        super.withdraw(amount)
        notifyObservers()
    }
}


class AccountReporter extends Observer[Account] {
    def receiveUpdate(account: Account) =
        println("Observed balance change: "+account.balance)
}

Let's see it in action:

scala> val oa = new ObservedAccount(100.0)
oa: ObservedAccount = ObservedAccount@3f947e20

scala> val ar = new AccountReporter
ar: AccountReporter = AccountReporter@6ea70a98

scala> oa.addObserver(ar)

scala> oa.deposit(40.0)
Observed balance change: 140.0

scala> oa.withdraw(40.0)
Observed balance change: 100.0
Brian
  • 20,195
  • 6
  • 34
  • 55
3

Building on Brian's answer: I find it unnecessary to have a separate Observer[S] trait, simply S => Unit is good enough:

trait Subject[S] {
  this: S =>
  private var observers: List[S => Unit] = Nil
  def addObserver(observer: S => Unit) = observers = observer :: observers
  def notifyObservers() = observers.foreach(_.apply(this))
}

class Account(initialBalance: Double) {
  private var currentBalance = initialBalance
  def balance = currentBalance
  def deposit(amount: Double) = currentBalance += amount
  def withdraw(amount: Double) = currentBalance -= amount
}

class ObservedAccount(initialBalance: Double) extends Account(initialBalance)
  with Subject[Account] {

  override def deposit(amount: Double) = {
    super.deposit(amount)
    notifyObservers()
  }
  override def withdraw(amount: Double) = {
    super.withdraw(amount)
    notifyObservers()
  }
}

class AccountReporter {
  def receiveUpdate(account: Account) =
    println("Observed balance change: " + account.balance)
}

object Main extends App {
  println("start app")
  val oa = new ObservedAccount(100.0)
  val ar = new AccountReporter
  oa.addObserver(ar.receiveUpdate _)
  oa.deposit(40.0)
  oa.deposit(60.0)
  println("stop  app")
}


/**
      a copy paste observer pattern scala mini-app
      sbt run should produce:
      [info] Running app.Main
      start app
      Observed balance change: 140.0
      Observed balance change: 200.0
      stop  app
  */
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53
Landei
  • 54,104
  • 13
  • 100
  • 195
2

Sorry to answer a question with a question. Have you read your answer here or the more interesting answer here and here? Someone should compile a reading list, if they haven't. Did Coursera have a reading list?

som-snytt
  • 39,429
  • 2
  • 47
  • 129
1

this is a Scala self-type ( see http://www.scala-lang.org/node/124 ). It expresses a requirement that all concrete implementations of the trait Subject[S] must also conform to the type S. That is, every observable Subject[S] is itself an S.

This is reasonable for the Observer pattern -- it is the observable subject itself that should have registration and notify methods, so the subject consistent with an observer of S should itself be an S.

Steve Waldman
  • 13,689
  • 1
  • 35
  • 45