Observer Pattern

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Observer Pattern

PabloS
Folks, I'm trying to implement an Observer Pattern in Dolphin
Smalltalk, but all the documentation I have is for other distribution
(VisualAge, etc).

What I'm trying to do is an event correlation object, when some
variable from the first object change, the other objects gets the
change an follow the first object.

I found many examples in Internet, but I'm a litle confuse about the
triggers and addDependents, etc.. I would appreciate if you have a
clear example how to implement it in dolphin.

Thanks.
Pablo


Reply | Threaded
Open this post in threaded view
|

Re: Observer Pattern

Ian Bartholomew-19
Pablo,

> I found many examples in Internet, but I'm a litle confuse about the
> triggers and addDependents, etc.. I would appreciate if you have a
> clear example how to implement it in dolphin.

In Dolphin any object can trigger an event and any other object can register
to receive a notification from the source object when the event is
triggered.

So, say you have a class called MyWidget and you want to know when a
MyWidget object is modified.  If all changes to MyWidget can be channeled
through one method then you would simply have ...

MyWidget>>modify
    (code to modify this MyWidget object)
    self trigger: #changed

If there were multiple ways (methods) to change MyWidget then each method
could trigger the same event _or_ each method could trigger a different
event

MyWidget>>modifyA
    (code to modify the A aspect in this MyWidget object)
    self trigger: #changedA

MyWidget>>modifyB
    (code to modify the B aspect in this MyWidget object)
    self trigger: #changedB

You can also pass one or more arguments so the object triggering the event
could either pass the new value or pass itself so that any object being
notified would know who triggered it [1]

MyWidget>>modifyA
    (code to modify the A aspect MyWidget object)
    "NOTE the colon after the Symbol which indicates an argument"
    self trigger: #changedA: with: self newAValue

or

MyWidget>>modify
    "code to modify this MyWidget object"
    self trigger: #changed: with: self



Objects that need to be notified when an object changes would normally
register themselves when the MyWidget is created.

MyController>>initialize
    myWidget := MyWidget new.
    myWidget when: #changed send: #onWidgetChanged to: self

MyController>>onWidgetChanged
    "myWidget has changed"

With an argument.....

MyController>>initialize
    myWidget := MyWidget new.
    "NOTE a colon at the end of each Symbol"
    myWidget when: #changed: send: #onWidgetChanged: to: self

MyController>>onWidgetChanged: anArgument
    "myWidget has changed"

[1] Note that the event symbol used when passing arguments doesn't _need_ to
include colons (i.e. be a keyword selector) but it does make thing easier
when reading the code. For example, the following would work and pass two
arguments

MyWidget>>modify
    "code to modify this MyWidget object"
    self trigger: #changed with: 1 with: 2

MyController>>initialize
    myWidget := MyWidget new.
    myWidget when: #changed send: #onWidgetChanged:with: to: self

MyController>>onWidgetChanged: arg1 with: arg2
   "arg1 == 1"
   "arg2 == 2"



The dependency mechanism is a separate framework and is not really used in
Dolphin.

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: Observer Pattern

PabloS
Thanks Ian, it works well!. What I'm trying to do is to wire more than one
object to many objects, in your example I understand that is for one object
follow by many.

What I have here, is  very similar to an auction, with many clients with
many auctions, and when a price o something from any of the auctions change
all the clients needs to be notified. How can I register all the clients
with all the auctions ?

Thanks again..
PabloS


"Ian Bartholomew" <[hidden email]> wrote in message
news:[hidden email]...
> Pablo,
>
> > I found many examples in Internet, but I'm a litle confuse about the
> > triggers and addDependents, etc.. I would appreciate if you have a
> > clear example how to implement it in dolphin.
>
> In Dolphin any object can trigger an event and any other object can
register

> to receive a notification from the source object when the event is
> triggered.
>
> So, say you have a class called MyWidget and you want to know when a
> MyWidget object is modified.  If all changes to MyWidget can be channeled
> through one method then you would simply have ...
>
> MyWidget>>modify
>     (code to modify this MyWidget object)
>     self trigger: #changed
>
> If there were multiple ways (methods) to change MyWidget then each method
> could trigger the same event _or_ each method could trigger a different
> event
>
> MyWidget>>modifyA
>     (code to modify the A aspect in this MyWidget object)
>     self trigger: #changedA
>
> MyWidget>>modifyB
>     (code to modify the B aspect in this MyWidget object)
>     self trigger: #changedB
>
> You can also pass one or more arguments so the object triggering the event
> could either pass the new value or pass itself so that any object being
> notified would know who triggered it [1]
>
> MyWidget>>modifyA
>     (code to modify the A aspect MyWidget object)
>     "NOTE the colon after the Symbol which indicates an argument"
>     self trigger: #changedA: with: self newAValue
>
> or
>
> MyWidget>>modify
>     "code to modify this MyWidget object"
>     self trigger: #changed: with: self
>
>
>
> Objects that need to be notified when an object changes would normally
> register themselves when the MyWidget is created.
>
> MyController>>initialize
>     myWidget := MyWidget new.
>     myWidget when: #changed send: #onWidgetChanged to: self
>
> MyController>>onWidgetChanged
>     "myWidget has changed"
>
> With an argument.....
>
> MyController>>initialize
>     myWidget := MyWidget new.
>     "NOTE a colon at the end of each Symbol"
>     myWidget when: #changed: send: #onWidgetChanged: to: self
>
> MyController>>onWidgetChanged: anArgument
>     "myWidget has changed"
>
> [1] Note that the event symbol used when passing arguments doesn't _need_
to

> include colons (i.e. be a keyword selector) but it does make thing easier
> when reading the code. For example, the following would work and pass two
> arguments
>
> MyWidget>>modify
>     "code to modify this MyWidget object"
>     self trigger: #changed with: 1 with: 2
>
> MyController>>initialize
>     myWidget := MyWidget new.
>     myWidget when: #changed send: #onWidgetChanged:with: to: self
>
> MyController>>onWidgetChanged: arg1 with: arg2
>    "arg1 == 1"
>    "arg2 == 2"
>
>
>
> The dependency mechanism is a separate framework and is not really used in
> Dolphin.
>
> --
> Ian
>
> Use the Reply-To address to contact me.
> Mail sent to the From address is ignored.
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Observer Pattern

Louis Sumberg-2
Pablo,

> What I have here, is  very similar to an auction, with many clients with
> many auctions, and when a price o something from any of the auctions
change
> all the clients needs to be notified. How can I register all the clients
> with all the auctions ?

Based on the above spec, I'm not sure observers are needed.  Presumably, a
client joins an auction at some point in time.  The auction object can keep
a list of active clients (in an instance variable).  When the price of some
object in the auction is changed, the auction sends a message to each of the
clients it knows of.

Auction>>addClient: aClient
clients add: aClient.

Auction>>price: aNumber item: anObject
"Set anObject's price to aNumber and notify all clients."
anObjet price: aNumber.
clients do: [:client |
    client price: aNumber item: anObject auction: self].

Client>>price: aNumber item: anObject auction: anAuction
<client logic goes here>

-- Louis


Reply | Threaded
Open this post in threaded view
|

Re: Observer Pattern

Ian Bartholomew-19
In reply to this post by PabloS
Pablo,

> What I have here, is  very similar to an auction, with many clients
> with many auctions, and when a price o something from any of the
> auctions change all the clients needs to be notified. How can I
> register all the clients with all the auctions ?

I'm not quite sure what you are asking here?

An object that triggers events (your auction) has no interest in the number
of objects (your clients) that have registered to receive the events.  It
just triggers the event and lets the system sort out who needs to know - if
anyone.

A client can register with any number of auctions.  As all the auctions will
trigger the same event then the client needs to know which auction it was -
this is when passing the triggerer as an argument is useful....

Auction>>newBid: aNumber
    (code to accept the bid)
    self trigger: #newBid with: self with: aNumber

Bidder>>initialize
    (all auctions) when: #newBid send: #onNewBidInAuction:bid: to: self

Bidder>>onNewBidInAuction: anAuction bid: aNumber
    (someone had made a bid of aNumber in anAuction)

The bit you may be missing is some sort of "Manager" object that holds the
Auctions and Bidders.  If you want all auctions to be accessible to all
bidders then that would just be a case of

AuctionManager>>addBidder: aString
    | bidder |
   bidders add: (bidder := Bidder new name: aString).
    auctions do: [:each |
        each when: #newBid send: #onNewBidInAuction:bid: to: bidder]

AuctionManager>>addAuction: anObject
    | auction|
    auctions add: (auction:= Auction new item: anObject).
    bidders do: [:each |
        auction when: #newBid send: #onNewBidInAuction:bid: to: each]

That will link all auctions to all bidders and whenever a new bid is made in
an auction then all the bidders will be notified.  If a bidder wasn't
interested in an auction then you could have a bit mor code that only linked
the two if you knew the bidder would be interested.

You could also work it so the "manager" intercepts all new bid notifications
....

An Auction triggers an event that is only received by the AuctionManager
The AuctionManager then triggers an event that is used by the Bidders.

This would give a bigger separation between the Auctions and Bidders and
would allow the AuctionManager to do any extra processing (log bids, check
bidders limits etc).

As I said above, I'm not really sure which bit is causing you a problem so
feel free to ask again.

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: Observer Pattern

PabloS
Thanks Ian/Louis, It works well..

"Ian Bartholomew" <[hidden email]> wrote in message
news:[hidden email]...

> Pablo,
>
> > What I have here, is  very similar to an auction, with many clients
> > with many auctions, and when a price o something from any of the
> > auctions change all the clients needs to be notified. How can I
> > register all the clients with all the auctions ?
>
> I'm not quite sure what you are asking here?
>
> An object that triggers events (your auction) has no interest in the
number
> of objects (your clients) that have registered to receive the events.  It
> just triggers the event and lets the system sort out who needs to know -
if
> anyone.
>
> A client can register with any number of auctions.  As all the auctions
will
> trigger the same event then the client needs to know which auction it
was -

> this is when passing the triggerer as an argument is useful....
>
> Auction>>newBid: aNumber
>     (code to accept the bid)
>     self trigger: #newBid with: self with: aNumber
>
> Bidder>>initialize
>     (all auctions) when: #newBid send: #onNewBidInAuction:bid: to: self
>
> Bidder>>onNewBidInAuction: anAuction bid: aNumber
>     (someone had made a bid of aNumber in anAuction)
>
> The bit you may be missing is some sort of "Manager" object that holds the
> Auctions and Bidders.  If you want all auctions to be accessible to all
> bidders then that would just be a case of
>
> AuctionManager>>addBidder: aString
>     | bidder |
>    bidders add: (bidder := Bidder new name: aString).
>     auctions do: [:each |
>         each when: #newBid send: #onNewBidInAuction:bid: to: bidder]
>
> AuctionManager>>addAuction: anObject
>     | auction|
>     auctions add: (auction:= Auction new item: anObject).
>     bidders do: [:each |
>         auction when: #newBid send: #onNewBidInAuction:bid: to: each]
>
> That will link all auctions to all bidders and whenever a new bid is made
in
> an auction then all the bidders will be notified.  If a bidder wasn't
> interested in an auction then you could have a bit mor code that only
linked
> the two if you knew the bidder would be interested.
>
> You could also work it so the "manager" intercepts all new bid
notifications

> ....
>
> An Auction triggers an event that is only received by the AuctionManager
> The AuctionManager then triggers an event that is used by the Bidders.
>
> This would give a bigger separation between the Auctions and Bidders and
> would allow the AuctionManager to do any extra processing (log bids, check
> bidders limits etc).
>
> As I said above, I'm not really sure which bit is causing you a problem so
> feel free to ask again.
>
> --
> Ian
>
> Use the Reply-To address to contact me.
> Mail sent to the From address is ignored.
>
>