forked from steger/pr3-sose2026
observer design pattern
parent
05b5b27155
commit
54886195c6
|
|
@ -0,0 +1,3 @@
|
||||||
|
module gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
patterns.Subject
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCounter() Counter {
|
||||||
|
return Counter{patterns.NewSubject(), 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Counter) Inc() {
|
||||||
|
c.Count++
|
||||||
|
c.Notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Counter) Dec() {
|
||||||
|
c.Count--
|
||||||
|
c.Notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CountPrinter struct {
|
||||||
|
counter *Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cp *CountPrinter) Update() {
|
||||||
|
fmt.Println("count updated to ", cp.counter.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CounterHistory struct {
|
||||||
|
counter *Counter
|
||||||
|
history []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch *CounterHistory) Update() {
|
||||||
|
ch.history = append(ch.history, ch.counter.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
counter := NewCounter()
|
||||||
|
|
||||||
|
history := CounterHistory{&counter, []int{}}
|
||||||
|
counter.Register(&history)
|
||||||
|
|
||||||
|
printer := CountPrinter{&counter}
|
||||||
|
counter.Register(&printer)
|
||||||
|
printerRegistered := true
|
||||||
|
|
||||||
|
var input string
|
||||||
|
for {
|
||||||
|
fmt.Print("Enter a command (+: increment, -: decrement, q: quit): ")
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
|
||||||
|
if len(input) != 1 {
|
||||||
|
fmt.Println("Please enter a single character.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch input[0] {
|
||||||
|
case '+':
|
||||||
|
counter.Inc()
|
||||||
|
case '-':
|
||||||
|
counter.Dec()
|
||||||
|
case 'p':
|
||||||
|
if printerRegistered {
|
||||||
|
counter.Unregister(&printer)
|
||||||
|
} else {
|
||||||
|
counter.Register(&printer)
|
||||||
|
}
|
||||||
|
printerRegistered = !printerRegistered
|
||||||
|
case 'q':
|
||||||
|
fmt.Println("Exiting...")
|
||||||
|
fmt.Println(history.history)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fmt.Println("Unknown command. Use '+', '-', or 'q'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package patterns
|
||||||
|
|
||||||
|
type Observer interface {
|
||||||
|
Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Subject interface {
|
||||||
|
Register(o Observer)
|
||||||
|
Unregister(o Observer)
|
||||||
|
Notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
type subject struct {
|
||||||
|
observers map[Observer]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubject() Subject {
|
||||||
|
return &subject{
|
||||||
|
observers: make(map[Observer]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subject) Register(o Observer) {
|
||||||
|
s.observers[o] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subject) Unregister(o Observer) {
|
||||||
|
delete(s.observers, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subject) Notify() {
|
||||||
|
for o := range s.observers {
|
||||||
|
o.Update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package patterns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockObserver struct {
|
||||||
|
callCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mo *MockObserver) Update() {
|
||||||
|
mo.callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubject_Notify(t *testing.T) {
|
||||||
|
type observer struct {
|
||||||
|
MockObserver
|
||||||
|
toRegister bool
|
||||||
|
expectedCallcount int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
observers []*observer
|
||||||
|
}{
|
||||||
|
{"No observers", []*observer{}},
|
||||||
|
{"Single unregistered observer", []*observer{&observer{MockObserver{0}, false, 0}}},
|
||||||
|
{"Single registered observer", []*observer{&observer{MockObserver{0}, true, 1}}},
|
||||||
|
{"Two registered observers", []*observer{&observer{MockObserver{0}, true, 1}, &observer{MockObserver{0}, true, 1}}},
|
||||||
|
{"One registered and one unregistered observers", []*observer{&observer{MockObserver{0}, true, 1}, &observer{MockObserver{0}, false, 0}}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := patterns.NewSubject()
|
||||||
|
|
||||||
|
for _, o := range tt.observers {
|
||||||
|
if o.toRegister {
|
||||||
|
s.Register(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Notify()
|
||||||
|
|
||||||
|
for _, o := range tt.observers {
|
||||||
|
if o.callCount != o.expectedCallcount {
|
||||||
|
t.Errorf("%s: expected a callcount of %v, actual: %v", tt.name, o.expectedCallcount, o.callCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset callcount & unregister
|
||||||
|
o.callCount = 0
|
||||||
|
s.Unregister(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
//call notify when all observers are unregistered
|
||||||
|
s.Notify()
|
||||||
|
|
||||||
|
//expect a callcount of 0
|
||||||
|
for _, o := range tt.observers {
|
||||||
|
if o.callCount != 0 {
|
||||||
|
t.Errorf("%s: expected a callcount of 0, actual: %v", tt.name, o.callCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue