0106: D-language Snippets II - A Generic Observer

Picking up from last time…

We discussed the difference between the Observer and Singleton patterns, then took a closer look at both the generic Singleton and a specific example of the Singleton at work. This time, we’ll do the same thing with the Observer.

The Observer Pattern

Note: There is no UI for this example, just the output to the terminal.

Results of this example:
Current example terminal output
Current example terminal output (click for enlarged view)

And because there’s no UI, we’ll start with…

The main() Function

All we do in main() is instantiate all the bits we need, then run the test a few times.

void main()
{
	// initialize objects
	Subject subject = new Subject();
	Observer observer1 = new Observer("First Observer...", subject);
	Observer observer2 = new Observer("Second Observer..", subject);
	
	// change state
	foreach(int i; 0..4)
	{
		writeln("\nChanging state of Subject...");
		subject.change();
		observer1.reportState();
		observer2.reportState();
	}
	
} // main()

In the preamble, we instantiate three objects—one to be watched, and two others to do the watching. (Make sure to instantiate the subject of observation first.)

Next comes the heart of our demo, a loop that:

  • toggles the state of the watched object between true and false a few times, and
  • calls upon each of the Observers to tell us what they see.

The loop spits out proof that the dynamic between Observers and watched object is working.

The Observer Class

Now we get into the actual Observer code, starting with the class named for the pattern:

class Observer
{
	string idString;
	bool subjectState;
	
	this(string id, Subject subject)
	{
		idString = id;
		subject.addObserver(this);
		
	} // this()
	
	
	void reactToSubjectStateChange(bool newState)
	{
		subjectState = newState;
		
	} // reactToSubjectStateChange()
	
	
	void reportState()
	{
		writeln("Viewing from ", idString, ". The subject's state is now: ", subjectState);

	} // reportState()
	
} // class Observer

In the preamble we find an ID. It’s not strictly necessary for production-level code… unless you need to track which observer is watching at any given time. For a demonstration such as this, it can also give us proof that the mechanism is working.

In the reactToSubjectStateChange() function the Observer’s subjectState Boolean is the only thing we update, but it stands in for whatever housekeeping the Observer would ordinarily do when the watched object reports changes.

Note: this function is never called by anything other than the watched object. This is because we only want an Observer to react to changes reported by the watched object… in other words, only when new information comes straight from the horse’s mouth.

Now let’s look at…

The Subject/Watched/Observed Class

In a nutshell, any watched object will have:

  • one or more dynamic properties,
  • a mechanism for adding (and, in real-world use cases, perhaps, removing) Observers, and
  • a second mechanism for reporting to the Observers any time a change takes place in one of its dynamic properties.

Preamble

bool switcherState;
Observer[] observers;

So, as expected, we have a bit of data that the Observers want to track and an array that will hold a list of those Observers.

The Constructor

This constructor is pretty straightforward:

this()
{
	switcherState = false;
	
} // this()

As can be seen, it just sets the initial state of the data.

The addObserver Function

void addObserver(Observer observer)
{
	observers ~= observer;
	observer.reactToSubjectStateChange(switcherState);
	
} // addObserver()

Here we add a new Observer to the watched object’s list and get it up to speed right away as to the state of the switcherState property. If we had a bunch of properties that Observers needed to be aware of, we can pass as many as necessary to the reactToSubjectStateChange() function.

The updateAll Function

void updateAll()
{
	foreach(observer; observers)
	{
		observer.reactToSubjectStateChange(switcherState);
			
	}
		
} // updateAll()

This steps through the list of Observers and informs them of the change. Observers can have different implementations of reactToSubjectStateChange() which allows each to react in its own unique way to changes in the watched object.

The change Function

And finally, we have:

void change()
{
	if(switcherState == true)
	{
		switcherState = false;
	}
	else
	{
		switcherState = true;
	}
	
	updateAll();
	
} // change()

This is our change-for-change’s-sake function and is here for demonstration purposes only. It forces a change that the entire system then reacts to.

Conclusion

This article is running long, so we’ll have to wait until next time to look over the Observer at work example.

Take care, and see you then.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant