Reputation: 6452
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
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
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
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
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