Skip to content

Commit 065ae23

Browse files
sha1sumtmrts
authored andcommitted
Implement Observer pattern
1 parent befd460 commit 065ae23

File tree

3 files changed

+141
-1
lines changed

3 files changed

+141
-1
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ __Behavioral Patterns__:
4242
| TODO: [Command](behavioral/command.md) | Bundles a command and arguments to call later |
4343
| TODO: [Mediator](behavioral/mediator.md) | Connects objects and acts as a proxy |
4444
| TODO: [Memento](behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state |
45-
| TODO: [Observer](behavioral/observer.md) | Provide a callback for notification of events/changes to data |
45+
| [Observer](behavioral/observer.md) | Provide a callback for notification of events/changes to data |
4646
| TODO: [Registry](behavioral/registry.md) | Keep track of all subclasses of a given class |
4747
| TODO: [State](behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state |
4848
| [Strategy](behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime |

Diff for: behavioral/observer.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Observer Pattern
2+
3+
The [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows a type instance to "publish" events to other type instances ("observers") who wish to be updated when a particular event occurs.
4+
5+
## Implementation
6+
7+
In long-running applications—such as webservers—instances can keep a collection of observers that will receive notification of triggered events.
8+
9+
Implementations vary, but interfaces can be used to make standard observers and notifiers:
10+
11+
```go
12+
type (
13+
// Event defines an indication of a point-in-time occurrence.
14+
Event struct {
15+
// Data in this case is a simple int, but the actual
16+
// implementation would depend on the application.
17+
Data int64
18+
}
19+
20+
// Observer defines a standard interface for instances that wish to list for
21+
// the occurrence of a specific event.
22+
Observer interface {
23+
// OnNotify allows an event to be "published" to interface implementations.
24+
// In the "real world", error handling would likely be implemented.
25+
OnNotify(Event)
26+
}
27+
28+
// Notifier is the instance being observed. Publisher is perhaps another decent
29+
// name, but naming things is hard.
30+
Notifier interface {
31+
// Register allows an instance to register itself to listen/observe
32+
// events.
33+
Register(Observer)
34+
// Deregister allows an instance to remove itself from the collection
35+
// of observers/listeners.
36+
Deregister(Observer)
37+
// Notify publishes new events to listeners. The method is not
38+
// absolutely necessary, as each implementation could define this itself
39+
// without losing functionality.
40+
Notify(Event)
41+
}
42+
)
43+
```
44+
45+
## Usage
46+
47+
For usage, see [observer/main.go](observer/main.go) or [view in the Playground](https://play.golang.org/p/cr8jEmDmw0).

Diff for: behavioral/observer/main.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Package main serves as an example application that makes use of the observer pattern.
2+
// Playground: https://play.golang.org/p/cr8jEmDmw0
3+
package main
4+
5+
import (
6+
"fmt"
7+
"time"
8+
)
9+
10+
type (
11+
// Event defines an indication of a point-in-time occurrence.
12+
Event struct {
13+
// Data in this case is a simple int, but the actual
14+
// implementation would depend on the application.
15+
Data int64
16+
}
17+
18+
// Observer defines a standard interface for instances that wish to list for
19+
// the occurrence of a specific event.
20+
Observer interface {
21+
// OnNotify allows an event to be "published" to interface implementations.
22+
// In the "real world", error handling would likely be implemented.
23+
OnNotify(Event)
24+
}
25+
26+
// Notifier is the instance being observed. Publisher is perhaps another decent
27+
// name, but naming things is hard.
28+
Notifier interface {
29+
// Register allows an instance to register itself to listen/observe
30+
// events.
31+
Register(Observer)
32+
// Deregister allows an instance to remove itself from the collection
33+
// of observers/listeners.
34+
Deregister(Observer)
35+
// Notify publishes new events to listeners. The method is not
36+
// absolutely necessary, as each implementation could define this itself
37+
// without losing functionality.
38+
Notify(Event)
39+
}
40+
)
41+
42+
type (
43+
eventObserver struct{
44+
id int
45+
}
46+
47+
eventNotifier struct{
48+
// Using a map with an empty struct allows us to keep the observers
49+
// unique while still keeping memory usage relatively low.
50+
observers map[Observer]struct{}
51+
}
52+
)
53+
54+
func (o *eventObserver) OnNotify(e Event) {
55+
fmt.Printf("*** Observer %d received: %d\n", o.id, e.Data)
56+
}
57+
58+
func (o *eventNotifier) Register(l Observer) {
59+
o.observers[l] = struct{}{}
60+
}
61+
62+
func (o *eventNotifier) Deregister(l Observer) {
63+
delete(o.observers, l)
64+
}
65+
66+
func (p *eventNotifier) Notify(e Event) {
67+
for o := range p.observers {
68+
o.OnNotify(e)
69+
}
70+
}
71+
72+
func main() {
73+
// Initialize a new Notifier.
74+
n := eventNotifier{
75+
observers: map[Observer]struct{}{},
76+
}
77+
78+
// Register a couple of observers.
79+
n.Register(&eventObserver{id: 1})
80+
n.Register(&eventObserver{id: 2})
81+
82+
// A simple loop publishing the current Unix timestamp to observers.
83+
stop := time.NewTimer(10 * time.Second).C
84+
tick := time.NewTicker(time.Second).C
85+
for {
86+
select {
87+
case <- stop:
88+
return
89+
case t := <-tick:
90+
n.Notify(Event{Data: t.UnixNano()})
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)