1
0
Fork 0

observer design pattern

main^2
Sebastian Steger 2025-08-20 11:31:53 +00:00
parent 05b5b27155
commit 54886195c6
4 changed files with 189 additions and 0 deletions

View File

@ -0,0 +1,3 @@
module gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern
go 1.25.0

View File

@ -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'.")
}
}
}

View File

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

View File

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