Saturday, October 24, 2009

Observer Pattern in MFC

As I walk through the different design concepts of the Design Pattern book by GoF, I usually try to find from my past experience about any practical implementation of these patterns. This way I found out one practical implementation of the Observer Pattern in microsoft foundation class (MFC) library which I would like to share with you.

Before explaining the application of Observer Pattern in MFC, let me give you a brief introduction of this pattern. Observer pattern helps you in notifying and updating all the dependent objects, if one object changes state. This pattern has two main objects – Subject and Observer. Multiple number of observers can be attached to a particular Subject. All the observers are notified and in turn gotten updated if the Subject changes its state. This is also known as the publish-subscribe. The subject is the publisher of changes, and the Observers are the subscribers to those changes.

The class diagram will look like the following.



And the sequence diagram of this pattern is like this.



What these two diagrams essentially depict is that in the Observer Pattern, we have a Subject, which can attach one or more Observers through its Attach() function. Whenever it changes its state it Notifies all the attached Observers through its Notify function. The observers in turn synchronize their states with that of the Subject through the GetSubjectState function.

Now let me dissect the MFC's Document-View architecture and explain how the Observer Pattern has been implemented there. In MFC, the Document acts as the Subject and the views attached to it act as the observers. The Document (Subject) has to notify and update the views (observers) whenever it changes its state. This is done through the function UpdateAllViews () of the CDocument class.

Let me show you the actual code of the UpdateAllViews from doccore.cpp under the MFC folder.

void
CDocument :: UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)
{
ASSERT(pSender == NULL || !m_viewList.IsEmpty());

POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
if (pView != pSender)
pView->OnUpdate(pSender, lHint, pHint);
}
}


As you can see from the code that UpdateAllViews function actually traverses through all the views attached through it and call the view's OnUpdate function. Hence UpdateAllViews acts as the Notify function of the Observer pattern.

Let me show you the actual code of OnUpdate from viewcore.cpp. It goes like this:

void
CView :: OnUpdate(CView* pSender,LPARAM, CObject*)
{
ASSERT(pSender != this);
UNUSED(pSender);
Invalidate(TRUE);
}

As its is clear from the above code, that the OnUpdate function will actually invalidate the view area, which will force the views to redraw themselves synchronizing their states with the current state of the Document.

This is all about the notification from Document to View. But what about the Attach and Detach function of the Subject. We have similar functions in the Document class which are as follows:

void CDocument :: AddView(CView* pView)
{
ASSERT_VALID(pView);
ASSERT(pView->m_pDocument == NULL);
ASSERT(m_viewList.Find(pView, NULL) == NULL);
m_viewList.AddTail(pView);
ASSERT(pView->m_pDocument == NULL);
pView->m_pDocument = this;

OnChangedViewList();
}

void CDocument :: RemoveView(CView* pView)
{
ASSERT_VALID(pView);
ASSERT(pView->m_pDocument == this);

m_viewList.RemoveAt(m_viewList.Find(pView));
pView->m_pDocument = NULL;

OnChangedViewList();
}

As this is clear from the above listings that CDocument :: AddView is similar to the Attach function of the Subject, and RemoveView is like the Detach function as in the Observer Pattern.

Similar to the Observer Pattern, the Document has a list of its attached views through its m_viewList member and each view has a reference to its document through its m_pDocument member.

From the above discussion, it has become clear that the MFC's Document- View architecture is one of the applications of the Observer Pattern.

No comments: