diff --git a/go/04-design-pattern/main.go b/go/04-design-pattern/main.go index 777d855..ccbc7c0 100644 --- a/go/04-design-pattern/main.go +++ b/go/04-design-pattern/main.go @@ -6,25 +6,30 @@ import ( "gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns" ) +// Counter is a concrete implementation of the Subject interface, which maintains a count and notifies observers whenever the count changes type Counter struct { - patterns.Subject - Count int + patterns.Subject // Embedding the Subject interface allows Counter to inherit its methods and be treated as a Subject, enabling it to register observers and notify them of changes + Count int } +// NewCounter creates and returns a new instance of the Counter struct, initializing the embedded Subject using the NewSubject function from the patterns package func NewCounter() Counter { return Counter{patterns.NewSubject(), 0} } +// Inc increments the counter's count and notifies all registered observers of the change func (c *Counter) Inc() { c.Count++ c.Notify() } +// Dec decrements the counter's count and notifies all registered observers of the change func (c *Counter) Dec() { c.Count-- c.Notify() } +// CountPrinter is a concrete implementation of the Observer interface, which prints the current count whenever it receives an update notification from the Counter type CountPrinter struct { counter *Counter } @@ -33,21 +38,32 @@ func (cp *CountPrinter) Update() { fmt.Println("count updated to ", cp.counter.Count) } +var _ patterns.Observer = (*CountPrinter)(nil) // Compile-time assertion to ensure CountPrinter implements the Observer interface + +// CounterHistory is a concrete implementation of the Observer interface, which maintains a history of the counter's count values whenever it receives an update notification from the Counter type CounterHistory struct { counter *Counter history []int } +// Update appends the current count value to the history slice whenever it receives an update notification from the Counter func (ch *CounterHistory) Update() { ch.history = append(ch.history, ch.counter.Count) } +var _ patterns.Observer = (*CounterHistory)(nil) // Compile-time assertion to ensure CounterHistory implements the Observer interface + +// The main function demonstrates the usage of the Counter, CountPrinter, and CounterHistory in an interactive command-line application. +// It allows the user to increment or decrement the counter and see the updates printed, while also maintaining a history of count values. func main() { + // Create a new Counter instance, which will serve as the Subject in the Observer pattern counter := NewCounter() + // Create a new CounterHistory instance and register it as an observer of the counter to track the history of count values history := CounterHistory{&counter, []int{}} counter.Register(&history) + // Create a new CountPrinter instance and register it as an observer of the counter to print the current count whenever it changes printer := CountPrinter{&counter} counter.Register(&printer) printerRegistered := true @@ -68,6 +84,7 @@ func main() { case '-': counter.Dec() case 'p': + // Toggle the registration of the CountPrinter observer. If it's currently registered, unregister it; if it's currently unregistered, register it. if printerRegistered { counter.Unregister(&printer) } else { diff --git a/go/04-design-pattern/patterns/observer.go b/go/04-design-pattern/patterns/observer.go index 8e674ff..4474ee0 100644 --- a/go/04-design-pattern/patterns/observer.go +++ b/go/04-design-pattern/patterns/observer.go @@ -1,33 +1,43 @@ package patterns +// Observer pattern implementation in Go type Observer interface { Update() } +// Subject interface defines methods for registering, unregistering, and notifying observers type Subject interface { Register(o Observer) Unregister(o Observer) Notify() } +// Concrete implementation of the Subject interface. Lowercase 'subject' to make it unexported, as it will be created through the NewSubject function. type subject struct { - observers map[Observer]bool + observers map[Observer]bool // Using a map to store observers for efficient add/remove operations } +// NewSubject creates and returns a new instance of the subject func NewSubject() Subject { + // Needs to return a pointer to the subject struct to ensure that the same instance is modified when registering/unregistering observers return &subject{ observers: make(map[Observer]bool), } } +// Register adds an observer to the subject's set of observers +// Pointer receiver is used to modify the subject's state (the observers map) when registering an observer func (s *subject) Register(o Observer) { s.observers[o] = true } +// Unregister removes an observer from the subject's set of observers +// Pointer receiver is used to modify the subject's state (the observers map) when unregistering an observer func (s *subject) Unregister(o Observer) { delete(s.observers, o) } +// Notify calls the Update method on all registered observers to notify them of a change func (s *subject) Notify() { for o := range s.observers { o.Update() diff --git a/go/04-design-pattern/patterns/observer_test.go b/go/04-design-pattern/patterns/observer_test.go index d4582f0..cb9cd62 100644 --- a/go/04-design-pattern/patterns/observer_test.go +++ b/go/04-design-pattern/patterns/observer_test.go @@ -6,14 +6,17 @@ import ( "gitty.informatik.hs-mannheim.de/steger/pr3-code/go/04-design-pattern/patterns" ) +// MockObserver is a test implementation of the Observer interface, used to verify that the Notify method correctly calls the Update method on registered observers type MockObserver struct { callCount int } +// Update increments the callCount to track how many times the Update method has been called on this observer func (mo *MockObserver) Update() { mo.callCount++ } +// Table driven tests for the Subject's Notify method, covering various scenarios of registered and unregistered observers func TestSubject_Notify(t *testing.T) { type observer struct { MockObserver