Zavior
Zavior

Reputation: 6452

Implementing observer pattern

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))
}

Upvotes: 3

Views: 4884

Answers (4)

Landei
Landei

Reputation: 54574

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
  */

Upvotes: 3

Brian
Brian

Reputation: 20285

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

Upvotes: 9

som-snytt
som-snytt

Reputation: 39577

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?

Upvotes: 2

Steve Waldman
Steve Waldman

Reputation: 14073

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.

Upvotes: 1

Related Questions