Applying and Extending the Observer Pattern in THRSim11.

© 1998 Wilbert Bilderbeek, Harry Broeders and Alex van Rooijen.

One of the oldest and most well known design patterns is the "Observer" design pattern. (See Design Patterns: Elements of Reusable Object Oriented Software by E. Gamma, R. Helm, R. Johnson and J. Vlissides page 293.) This pattern makes it possible to design software modules that can be connected to each other by using a weak form of coupling. A program in which this pattern is applied is therefore easy to extend and maintain. The 68HC11 microcontroller simulator THRSim11 designed at the Rijswijk Institute of Technology (see makes extensive use of the observer design pattern.

This article starts with a classic description of this pattern including a modern C++ implementation. Next we describe how we extended the classical implementation to make this pattern easier to apply. We will then show how we applied this extended observer pattern in the THRSim11 68HC11 simulator. Not only to weakly couple the user interface with the rest of the application (the most common use of the observer pattern), but also to implement breakpoints and to simulate electronic components for example logical AND gates. This heavy use of the observer pattern resulted in an application that consists of several components or modules which are loosely coupled together. Each of these software components consist of several subjects and observers which can be connected to the observers and subjects of other components. This makes the THRSim11 68HC11 simulator program very easy to extend for the maintainers of the program (that's us:-). We can, for example, connect a simulated AND gate to the pins of the simulated 68HC11 as easily as you can connect a real AND gate to the pins of a real 68HC11.

When the students of the Rijswijk Institute of Technology started to use the THRSim11 68HC11 simulator almost immediately the following question did arise: "Can I connect my own VisualBasic/C++/Delphi/FillInYourFavorite program to the virtual pins of the simulated 68HC11, so I can simulate my own microcontroller application completely?". In the last part of this article we show how we used Microsoft's COM (Component Object Model) framework to make it possible to use the observer pattern across different applications. This makes it very easy to connect your own simulated components with our THRSim11 68HC11 simulator. Visit the THRSim11 home page mentioned earlier to get some examples.

The classic observer pattern.

Click here if you are already familiar with the classic observer pattern.

The classic class diagram of the observer pattern is given in figure 1. To discuss this pattern we use an example from our THRSim11 68HC11 microcontroller simulation program. In the software module that simulates the 68HC11 CPU several objects of the class Register represent the 68HC11 programming registers. The contents of a Register object can be displayed in several ways in the GUI module. The contents of the CCR (Condition Codes Register) for example can be displayed as a hexadecimal byte in a register window and as eight individual bits in the status bar. If the contents of a Register object is changed by simulating the execution of instructions, the different representations of this register in the GUI should change as well.

The code that is needed to update the register window and the status bar can not be included in the Register class because we want to keep this class independent of the GUI. A weak coupling between the GUI and the Register class can be realized by applying the observer design pattern as follows. Each class in the CPU module which objects should be "visible" in the GUI module (or in any other module) should be derived from the abstract base class Subject. See figure 1.

Figure 1. The Observer pattern
Figure 1. The Observer pattern.

Every member function in the class derived from Subject that changes the "visible" value must call the notify() member function which is defined in its base class Subject. It will be clear by now that the class Register should be derived from Subject. This makes the class Register completely independent from the GUI (the most weakly coupling that is possible!).

Every class in the GUI that represents an object from within the simulated 68HC11 should be derived from the abstract base class Observer. Each object from the class derived from Observer must contain a reference to the object it represents. By using this reference the GUI object can get or set the value of the object it represents. The coupling between the GUI and the simulated 68HC11 is not as weak as vice versa because the GUI must know how to preform this get and set. So the class Registerwindow used in the GUI should be derived from Observer. An object from the class RegisterWindow must attach itself to a Register object by calling this Subject's attach() member function with itself (this) as the only parameter. The RegisterWindow object must also connect itself to this Register object setting its concreteSubject reference.

Each Subject object contains a list of all Observer objects that are connected to this Subject. The class Observer has a pure virtual update() member function. This member function should be overridden in classes derived from Observer, such as RegisterWindow. This member function is, as we shall see shortly, called "automatically" when the value of a Register object changes. The RegisterWindow object can get the new value of the Register object via its concreteSubject reference by calling Register's get() member function (see figure 1). This new value should then be displayed in the register window. Each change of the value of a Register object leads to a call to the update() member function of each RegisterWindow that has attached itself to this Register. This is accomplished by the convention that every member function that changes the value of a Register object (for example set()) calls the notify() member function in its base class Subject. This notify() calls the pure virtual defined update() function for each Observer in its list of attached observers. This member function is overridden in the classes that are derived from Observer such as RegisterWindow. By applying the pattern we accomplish that the Register class has no knowledge about the number and the concrete type of the Observer objects in the GUI that represent (display) the value of this Register object.

Each ConcreteObserver such as RegisterWindow can also control the value it represents by calling the Register's set() member function via its concreteSubject reference. The RegisterWindow does not have to update its representation after changing the value of a Register object because this will be done "automatically". The call to Register::set() leads via Subject::notify() to a call to the RegisterWindow::update() member function which calls Register::get() to update its representation.

A straightforward implementation of this observer pattern is given in listing 1. The list of Observers maintained in Subject is implemented by using the standard C++ list (linked list) template as follows:

list<Observer*> observers;

The notify() member function is implemented as follows:

void notify() {
	for_each(observers.begin(), observers.end(), mem_fun(&Observer::update));

The template function for_each() is also included in the standard C++ library. Stroustrup explains this algorithm on page 523 of the third edition of his C++ programming book as follows:

A key benefit of the standard library algorithms is that they save the programmer from writing explicit loops. Loops can be tedious and error-prone. The for_each() algorithm does nothing but eliminate an explicit loop. It simply calls its third argument for all objects in a sequence specified by the first two arguments.

Because for_each() function needs a normal function as its third argument we have to use the mem_fun() template to transform the member function Register::update() to something that can be called as a normal function. Stroustrup explains this template on page 520 of his book as follows (slightly modified to fit our example):

The standard library mem_fun() template takes a pointer to a member function as its argument and produces something that can be called as a ordinary function which takes a pointer to the members class as a argument. The result of mem_fun(&Observer:update) takes a Observer* argument and returns whatever Observer::update() returns. The mem_fun() mechanism is important because it allows the standard algorithms (as for_each) to be used for containers of polymorphic objects (as list<Observer*>).

Because the member_fun() template was added rather late (31 july 1997, see in the STL (Standard Template Library, the original name for the library that is now included in the C++ standard library) this function is not supported by the Borland C++ compiler version 5.02 and not supported correctly by the Microsoft Visual C++ compiler version 5.0. See Note 1. The fixup we present (see listing1) can be used without problems in BC++5.02 and MVC++5.0.

Make the observer pattern easier to use.

When we evaluated the use of the classic implementation of the observer pattern (see figure 1). the following critical questions did arise:

We extended the observer pattern as shown in figure 2 and now we can answer yes to all of the above questions. As you can see we moved the link from ConcreteObserver to ConcreteSubject to their base classes (from Observer to Subject). This makes it possible to place the calls to attach() and detach() in respectively the constructor and destructor of Observer. If a Subject is destructed all attached Observers are notified and they invalidate their link. This avoids dangling references. (This is not shown in figure 2 but can be found in listing 2.) A ConcreteObserver can determine the Subject to which it is connected to by calling the getPtrToSubject() of its base class Observer. This member function throws an exception if this Subject is already destroyed so the ConcreteObserver can handle this situation properly.

Figure 2: Our extended implementation of the observer pattern.
Figure 2. The extended Observer pattern.

The template class Model<T> can be used to create a simple ConcreteSubject that encapsulates a single variable of type T. So there is no need to define a new derived class anymore, just instantiate this template. A very simple simulator module, for example, containing some logical pins can be created as follows.

class Simulator {
	Model<bool> pin1, pin2, pin3;
	void run() {
   		pin1.set(0); pin2.set(1); pin3.set(0);
	   	pin1.set(1); pin2.set(0); pin3.set(1);

If it bothers you that the data members are public remember than that their purpose is to serve as connection points. Connection points of software modules should be visible from outside just like the pins and connectors of hardware modules. These pins can be driven by calling their void set(bool) member function and can be read by calling their bool get() member function. Observers can react "automatically" when someone changes the value of a pin. If you need more functionality than is provided by this simple Model<T> template you can derive from this class and add whatever functionality you want. Later on in this article you will see that we did this to create a kind of Model that can be observed from within other applications.

The problem that arises when we want to observe more than one Subject from within a single ConcreteObserver has been avoided now because each ConcreteObserver only contains (in its base class) one link to a Subject. But this of cause does not solve the problem because we still might want to construct a component that is observing more than one Subject. The solution is to let such a component contain several (one for each Subject to observe) special ConcreteObservers. We designed the CallOnWrite<T,C> template to be used as such a special ConcreteObserver. A component class called C that observers one or more Model<T>s (possible different types of T) can be constructed as follows.

In this class C:

A component that is connected to pin1 and pin2 of an object of the Simulator class we gave as an example earlier and reports every value that is written to these pins is constructed as follows.

class Reporter {
   Reporter(Simulator& s):
      cow1(s.pin1, this, &Reporter::update1),
      cow2(s.pin2, this, &Reporter::update2) {}
   void update1() {
      cout<<"Pin 1 is made "<<cow1->get()<<endl;
   void update2() {
      cout<<"Pin 2 is made "<<cow2->get()<<endl;
   CallOnWrite<bool, Reporter> cow1, cow2;

Listing 2 shows exactly the same application as was shown in listing 1 but in listing 2 the extended observer pattern is used. You can see that more code is shifted out of the application code into the observer framework. The use of this observer framework is easier than the use of the original observer pattern.

Applications of the observer framework within THRSim11.

When the observer pattern becomes as easy to use as in our framework and when you realise its full potential you start to see Observers and Subjects everywhere. In figure 3 some applications of the observer pattern in our THRSim11 68HC11 simulation program are shown.

Figure 3: Using the observer pattern in THRSim11.
Figure 3. Using the Observer pattern in THRSim11.

The first two observers in this figure are edit controls that are contained in a window. These controls display the value of the 68HC11's Program Counter (PC). The first in decimal and the second in hexadecimal notation. These edit controls can also be used to change the value of the PC. Observer 3 shows a breakpoint. This breakpoint component checks its stop condition on every change of the object it observes (in this case the PC). When this stop condition becomes true, the simulation of instructions stops. Because we implemented breakpoints as a kind of Observer we made it possible to set a breakpoint on every simulator object that is visible in the GUI (registers, pins, memory locations, etc).

Observer 4 in figure 3, the disassembler window, also observes the PC. If the PC changes the appropriate line in this window changes its color to green. So the user can see the instruction that is currently executed. This line is also scrolled into the window if needed. The disassembler window not only observes the PC but it also observes all memory locations that are visible (in disassembled form) in this window. This makes changes in the code that is being executed immediately visible. This is crucial for simulating self modifying code. When the memory contents is also shown in hexadecimal form in another window, the relationship between the assembler code (shown in the disassembler window) and the machine code (shown in the memory window) is becoming very clear. If a user changes the hexadecimal machine code in the memory window the assembler code in the disassembler window changes accordingly. Because the disassembler window has a build in line assembler it also work the other way around. If the user changes the assembler code in the disassembler window, the machine code shown in the memory window changes immediately.

Listing 3 shows the implementation and use of the Breakpoint<Predicate> template that can be used to instantiate different kind of breakpoints. One of the so called Predicate function objects as defined in the C++ standard (see Stroustrup 3 ed. page 516) can be used as template argument and serves as stop condition for the breakpoint.

When we implemented the simulation of the 68HC11's on board devices (timer, serial ports, parallel ports, analog digital converter etc.) we also made extensive use of this observer pattern. By using this pattern we constructed independent components that are very easy to use and reuse. Listing 4 shows the implementation and use of two templates that can be used to instantiate every elementary component you can think of that transforms one or two input values to one output value. In fact these templates can transform any Predicate or Arithmetic function object defined in the C++ standard (see Stroustrup 3 ed. page 516 and 517) or that you define yourself into a component that can be used within the observer framework. This component observes its input values and when one of them changes the function object that is given as a template argument is used to calculate the new output value. The output is actually only set when its value changes to prevent unnecessary updates. This also eliminates the problem of circular everlasting updates that can occur when outputs are propagated back to the inputs of the same component.

Figure 4: Using the observer pattern to simulate logical components.
Figure 4. Using the Observer pattern to simulate logical components.

Figure 4 shows how to connect a simple simulator with some logical components. pin1 and pin2 of the simulator are used to drive an EXOR gate (in this case constructed out of several elementary gates). The output of this gate is connected to pin3 of the simulator. All the code that is needed to create this configuration is shown below.

typedef BinaryComponent<logical_and<bool> > And;
typedef BinaryComponent<logical_or<bool> > Or;
typedef UnaryComponent<logical_not<bool> > Not;

void main() {
	Simulator s;
	Pin node1, node2, node3, node4;
	Not not1(s.pin1, node1);
	Not not2(s.pin2, node2);
	And and1(node1, s.pin2, node3);
	And and2(node2, s.pin1, node4);
	Or or1(node3, node4, s.pin3);;

Listing 4 gives the implementations of the used templates and also shows a slightly more complicated application. As you can see now connecting a simulated logical or arithmetic gate to the simulated 68HC11 is far easier for us than connecting a real gate to a real 68HC11 is for you.

So we, the maintainers of the THRSim11 program, can easily maintain and extent our 68HC11 microcontroller simulation program. As time went by we extended the basic 68HC11 simulator with diverse external virtual components such as:

Figure 5: Using a NE555 timer for capacity measurements.
Figure 5. Using a NE555 timer for capacity measurements.

Extending the Observer pattern even further.

The users of the THRSim11 liked the extensions we programmed for them but they immediately added the remark that they would like it even better if they would be able to select from several virtual components and connect these components to the pins they want. Ultimately they would like to write their own extensions. This would give them a more beneficial product because they could completely simulate their own 68HC11 application.

This user demanded requirement was translated into a concrete question: "Is it possible to extend the simulator architecture so that separated software components can be coupled and decoupled without harming the observer design pattern architecture used within the simulator?" Furthermore, it must be possible to develop new software components separate (and independent) from the simulator. The user should be able to connect, in an arbitrary way, these new components to the I/O pins of the simulator, just like a real hardware component.

The connection between, for example a LED component and an output pin of the simulator is a perfect example of applying the observer design pattern. The LED software component is observing the output pin of the simulator and changes state if it gets a notify message from the output pin.

When the observer design pattern can be applied for connecting external software components to the simulator it will be very beneficial because:

For creating component software in Microsoft's Windows environment there is only one obvious solution: Microsoft's Component Object Model (COM).

COM is an important part of the Windows operating systems and delivers a base technology to dynamically couple server components to client applications. Furthermore, COM delivers a complete set of services to register and to search software components within a system. COM is the foundation for more familiar technologies like, OLE (Object Linking and Embedding), OLE automation and drag & drop. We present a short description of COM here. Click here if you are already familiar with COM.

The basic principles of the Component Object Model.

Microsoft has introduced the Component Object Model (COM) which is defining a program language independent standard component model. Every modern design language works in one way of the other with objects: Delphi, Visual Basic, Java and C++. The objects are all specified differently in these languages. Directly (re)using an object, written in C++, in Delphi is therefore a difficult task.COM solves this problem, it defines a language independent view on what an object is, how it can be instantiated, which methods it supports etc. This makes it possible for designers to develop and produce language independent software components that can be reused in a standard manner in many applications.

The COM standard differentiates between a client application and a server application. The client application instantiates COM components and a server (DLL or application) produces COM components. A globally unique identifier (GUID) identifies every released COM component. This is a 128-bit unique value that must be generated and published by the designer (releaser) of a COM component. COM delivers a service for generating GUID's. The client application uses a GUID to instantiate a COM object. The first step in instanciating a COM object is to pass the GUID from the client application to the COM layer of the Windows operating system. The COM layer uses the registry to localize the server that can produce (instantiate) the particular COM object. The server will return a pointer to the interface (=set of functions) of a COM object to the COM layer which will return this pointer to the client application. This mechanism is illustrated in figure 6.

Figure 6: Instantiating a COM object by a COM client application.
Figure 6. Instantiating a COM object by a COM client application.

The pointer returned by the COM layer to the client application is pointing to an interface structure wherefore the COM object delivers an implementation. The public member functions of the interface structure are always accessible in the same manner using this interface pointer. This is creating a language independent method for accessing COM objects, also called a binary interface. The interfaces of a COM component is being described using COM's interface definition language (IDL). Clients of a COM object can use this language to learn how to work with a particular COM object.

A common interface, every COM object must deliver an implementation for, is the so-called IUnknown interface. Notice that all COM interface definitions start with an I. The IUnknown interface consists of three functions: QueryInterface(), AddRef() and Release().

Using the functions AddRef() and Release(), a reference counter inside the COM object can respectively be incremented or decremented. When the internal reference count of the COM object reaches zero, the COM object destructs itself. This reference mechanism is built in because several applications can use the same instanciation of a COM object at the same time. When using the AddRef() and Release() functions correctly, when copying or discarding a pointer to the object, no problem will arise when one application destroys an object that is still being used by an other application.

The function QueryInterface() gives the client application the ability to navigate between the different interfaces a COM object supports. A client application can query a COM object to find out if it supporting (or having an implementation for...) for example interface ICustom. The COM object will, when it has an implementation for ICustom, returns a pointer to the ICustom interface, which the client can use to access the member functions of ICustom. Supporting several interfaces by a COM object implements a kind of interface inheritance.

Using interfaces a client application has the ability to access the functionality of a COM object. COM also defines a technology known as connectable objects. Using this technology a COM object is able to pass events (messages) to a client application. To implement this a COM object must support four different interfaces as described in the connectable objects specification. One of these interfaces is the IConnectionPoint interface. A client application can determine, using this interface, which outgoing interface the COM object supports. An outgoing interface is the opposite of a normal COM interface because the interface is delivered by the client application. The COM standard calls this kind of interfaces event sinks.

Using the IConnectionPoint member functions Advise() and Unadvise() a client application can connect its event sink interface to a COM object. When the client object connects to the COM object, the COM object is able to pass events to the client application. The connectable object technology is illustrated in figure 7.

Figure 7. Connectable objects.

Figure 7. Connectable objects.

Combining COM and the Observer pattern.

After a short study of the possibilities of existing COM technologies (like OLE automation) we concluded that it was not easy to apply these OLE techniques to the observer architecture of THRSim11. First of all the client application (simulator) would require mayor changes to support for example OLE automation. Secondly using a relatively slow OLE automation interface would result in great loss of performance. Performance is important in the simulator because every simulated clock cycle can cause a state change in a component if this component is connected to the CLK pin of the simulated 68HC11. Connecting to the CLK is necessary if the component, for example a function generator or a logic analyzer  needs to run in synchronization with the simulator.

Fortunate it is possible to use COM's "connectable objects specification" to implement an observer design pattern a-like coupling between the simulator software and an external software component. Using this extended observer framework within the simulator and for developing external components, it will be possible to connect external server components run-time to the client application (the simulator) using the observer design pattern as a coupling mechanism.

A COM server component that can be used by the THRSim11 68HC11 simulator must support the following functionality:

This functionality is gathered in the COM interface IComponent, we specially defined to implement our framework. Of course this COM observer framework must hide all COM details, query interfaces, reference counting etc., for our C++ applications.

To describe the design and use of the COM observer framework we will use the earlier described simple simulator example application. We will extend this application with an independent designed and build COM server component, an EXOR gate. The input pins of the EXOR gate will be connected to pin1 and pin2 of the simulator. The output pin of the EXOR gate will be connected to pin3 of the simulator.

Figure 8: An EXOR component connected to the simulator using COM.
Figure 8. An EXOR component connected to the simulator using COM.

You can see in figure 8 that the EXOR-component is observing pin1 and pin2 of the simulator. The EXOR receives a notify message when one of these pins is being driven by the simulator application. The EXOR port output pin drives, by sending a set message, pin3 of the simulator. Internally in the EXOR component the two input pins will be observed and when they change state a new value will be written to the output pin.

The first step to extend the simulator program with the EXOR component is to instantiate this COM component. The COM observer framework hides all details and we can simply create the COM object as follows:

COMComponent exor(CLSID_EXOR);

The only thing the client application has to provide to the constructor of the COMComponent object is a GUID (Globally Unique IDentifier) that is referencing to the EXOR component. Every COM component has this unique identifier by which it can be registered in a Microsoft Windows environment. The GUID is generated by the developer of the component and administered in the Windows registry when the user of the component installs it. When the COMComponent constructor is being called for the first time by the client application, the constructor initializes the COM library. You can see the COMComponent class as a wrapper for the COM IComponent interface.

When a COMComponent object is destructed its destructor member function releases the current COM component. When the COMComponent class determines that it is releasing the last COM object this client application was using, it automatically uninstalls the COM library. The above code fragment shows how easy it is to instantiate external COM objects. It is then possible to get a window handle and integrating the component into a client's graphical user interface. This will not be shown in this article because all example programs use a console user interface.

The next step is to examine the COM object for models it possesses which can be observed or controlled by the client application. Here we encounter a problem that is a bottleneck for any object model that defines communication between language independent applications, the datatype differences. We can not simple define a structure datatype in a COM object that can be observed by an arbitrary client application. Maybe the client application is not written in a C like language and does not understand structures at all, for example Visual Basic. For this reason we have to use the standard primitive datatypes defined by COM, which can be used in all languages. Some examples of these datatypes are: boolean, byte, word, long etc.

These datatypes are used to couple the client application with the COM object. We defined a special COM object, called COM model to encapsulate a primitive datatype for each kind of the datatypes defined in COM to fit them into our observer framework. These COM models can be instantiated, using the COM layer, by a client application or, more likely, by a COM component. The COM component can instantiate as many COM models as needed and expose them to a client application. The COM observer framework defines a custom interface for accessing these COM models by a client application. This interface enables the client application to iterate through the available COM models where the client application can specify what type of model it is searching for (for example bit or byte models).

The COM observer framework hides all details for a client application that wants to iterate through the COM models of a specific COM component. The client application can simply use the member function getNextBitModel() of the COMComponent class to iterate through all bit models possessed by the COM object. When this member function is called for the first time it returns a pointer to the first available bit COM model. The second call will return the next model and so on, until the last model has been reached resulting into an error code and resetting the iterator function in the COMComponent object.

A COM model itself has several similarities with a classical observer design pattern model (subject). The interface of a COM model consists out of a set() and a get() function. When the internal state of a model changes it passes a notify message to all its observers. The problem of independently passing notify messages to the observers of a COM model is solved by using COM's connectable objects technique. The COM connectable object specification is a technology that defines a standard way to connect a client application to a COM object which enables the COM object to pass messages (events) to the client application. To make use of connectable objects the client application must define and implement a COM compatible (binary) interface which can be used by the COM object to pass events.

As you have just seen, the function getNextBitModel() provides a pointer to a COM model. This pointer can be used by the client application to read the value of the COM model by using the get() member function. The client application can also write to the COM model by using the set() member function. But the client application can not observe the COM model because the COM model itself is not compatible with our observer framework. To make it possible for the client application to observe the COM model we create a specialized Model by inheriting from the Model<T> class. This new specialized class will be called COMModel<T> class. Figure 9 show the class hierarchy.

Figure 9: Class hierarchy of COMModel<T> and COM model.
Figure 9. Class hierarchy of COMModel<T> and COM model.

This specialized COMModel<T> class is a kind of wrapper for the real external COM model. The following code fragment shows how the client application gets a pointer to an external COM model and inserts this into a COMModel<T> object.

COMModel<IBoolModel> in1(exor.getNextBitModel());
COMModel<IBoolModel> in2(exor.getNextBitModel());
COMModel<IBoolModel> out(exor.getNextBitModel());

Listing 5 shows the constructor code of the COMModel<T> class. The first task of the constructor is to call its parent constructor to initialize the value inside its Model<T> base class with the actual value of the COM model. As the constructor code shows a special trick has been used to determine the type of a COM model when only a pointer to its interface is available. Without this trick it would be necessary to first call get()on a COMModel<T> object to obtain a pointer to the real COM model on which we must again call get() to obtain the value of the COM model. The COM model contains a typedef NativeType which defines the type encapsulated in the COM model. This makes it possible to immediately return the value of the data member encapsulated in the COM model when get() is called on a COMModel<T> object. The following IDL definition of an IBoolModel (bit model) interface shows that the native type of an IBoolModel COM object is bool.

	typedef bool NativeType;
	STDMETHOD_(bool, get) (THIS) PURE;

The next step in the constructor code for the COMModel<T> object is to store the pointer to the real COM model. Thereafter the constructor creates a ModelNotifySink object. When constructing this sink object it passes a callback pointer that the sink can use to pass events to the COMModel<T> object. An external COM model object can send events to the client application by using this ModelNotifySink object, which IDL interface definition is as follows:

DECLARE_INTERFACE_(IModelNotifySink, IUnknown)
	STDMETHOD(notifyUpdate) (THIS) PURE; // State of COMModel has changed
	STDMETHOD(notifyDestruct) (THIS) PURE; // COMModel is deleted (released)

The IModelNotifySink interface consists of two member functions. The member function notifyUpdate() which must be used by a COM model to notify the client application when its internal state has changed. The member function notifyDestruct() which must be called by a COM model when its internal reference counter reaches zero leading to a self-destruct (its observers must be informed about this).

The ModelNotifySink object passes these events directly to the COMModel<T> object by calling its member functions COMNotifyUpdate() respectively COMNotifyDestruct(). The COMNotifyUpdate() member function calls the inherited member function notify() so that all observers, observing this COM model are notified of an internal state change. The sink object will call the function COMNotifyDestruct() when an external COM model is going to destruct itself. The COMNotifyDestruct() function will call the destructor of the COMModel<T> class, this will automatically detach the COM model from all its observers. The observers can react on this if needed. When an observer tries to use the get() or set() member function after the COMModel<T> object has detached itself an exception is thrown. The destructor of the COMModel<T> object will furthermore use the unadvise() member function of the COM model to break the connection between the external COM model and the COMModel<T> object.

The connection between COMModel<T> and the external COM model is made in the constructor of the COMModel<T> object by calling the advise() member function on the COM model. The constructor code of COMModel<T> (see listing 5) shows the polymorphic features of COM. By using the standard interface IUnknown and its member function QueryInterface() it is possible for a client application to navigate from the IUnknown interface to the ICOMModel interface. This, custom defined, ICOMModel interface must be implemented and supported by all primitive COM model types, like IBoolModel, IByteModel, IWordModel etc. Using the ICOMModel interface it is possible to retrieve the name of a specific COM model (for example: "input pin IN1") and to use specific connectable objects interfaces.

But a user of the COM observer framework does not have to bother with all these details and can simply use the COMModel<T> object to observe, set and get the value of the external COM model. Listing 6 finally implements the configuration that was shown in figure 8. This is exactly the same application as was shown in listing 4, but now the EXOR gate is an external COM component which is developed completely independent from the client application. The pins of the simulator and the pins of the external EXOR are connected to each other by means of a Buffer component. These buffers are used to connect the internal and external models together. In this simple example the client application is aware of the existence of the external EXOR server component. The client application is furthermore responsible for making the appropriate connections.

In our THRSim11 68HC11 simulator application we want to give the users of our simulator the responsibility to create, install, and connect their own components. This is accomplished as follows. On startup the THRSim simulator searches the Windows registry for COM server components that can be connected to the simulator or, in other words, that support the IComponent interface.  The simulator determines the names and GUID's of these components and fills a specific part of the simulator menu bar with these names. If the user selects a component name from this menu the simulator instantiates the specified component using the GUID found in the registry. The simulator opens a new window in which the component can draw its graphical representation. At this moment the component is instantiated but is not connected to the simulator. The user can select the components window and choose the connect option from a pull up menu (right click the components window) or from the menu bar. This causes the simulator to examine which COM models this COM server possesses. A connect dialog box is setup which displays the names and types of all found COM models within this COM server on one site and the appropriate simulator models (subjects) on the other side. The user can make the connections he or she likes by using this dialog box. A COM BoolModel can, for example, be connected to every Model<bool> that exists in the simulator (all pins of the 68HC11). A COM ByteModel can, as another example, be connected to every Model<Byte> that exists in the simulator (every 8 bit register of the 68HC11 and every memory location). It is also possible to create new nodes to connect external components to each other using this connect dialog box. This enables the users of the THRSim11 68HC11 simulator to implement, install, and connect their own simulated components completely independent from us, the designers of the THRSim11 program.

As demonstrated the COM observer framework makes it very easy to transparently integrate COM components into a client application that uses an observer design pattern architecture. Using the COM observer framework it almost looks like application borders disappear.