Library Zone Articles
External Articles
Byte Size

Discovery Zone Catalogue
Diary
Links
Bookstore
Interactive Zone Ask the Gurus
Discussion Groups
Newsletters
Feedback
Etc Cartoons
Humour
COMpetition
Advertising
Site Builder ASP Web Ring ASP Web Ring

Power your site with idr newswire
The Developer's Resource & Community Site
COM XML ASP Java & Misc. NEW: VS.NET
International This Week Forums Author Central Find a Job

COM+ Patterns

Download print article

A Brief Introduction to Patterns

Creating software is one of the most challenging and rewarding jobs in the world. And fortunately for all of us there exists a wealth of tools, class libraries, SDKs, APIs and libraries designed to make implementation easier and more productive. But none of these can reverse the effects of a poor design, and let's face it, good design skills come with experience. There is no resourceful Wizard to make up for a lack of sound decisions during the design phase of a project.

It is because of this that software engineering is often described as a craft and an art as much as it is a science. Much like a carpenter or a blacksmith we are all at times best described as softwaresmiths. And like all "smithing", some aspects of software creation are extremely difficult to teach, they have to be experienced to truly learn. Good design skills fall into this category. And regrettably, the hard-won and often painful lessons gained by those who have spent their share of time designing software are often poorly communicated to others who could potentially benefit from the insight.

Fortunately, there does exist a structured and effective means for conveying insightful design creativity to the rest of the world. This powerful concept is known as Patterns.

The current use of the term "pattern" is derived from the writings of the architect Christopher Alexander who has written several books on the topic as it relates to urban planning and building architecture. Patterns are found everywhere and can be used to describe all sorts of systems. But it is only in recent times that they have been embraced by software development as a powerful organizational and descriptive tool.

So just what is a pattern?

Here is a formal definition:
A pattern is a named nugget of instructive information that captures the essential structure and insight of a successful family of proven solutions to a recurring problem that arises within a certain context and system of forces. (Whew!)

Ok, that was a mouthful, so how about a "less formal" definition. A pattern is a named description for a way to solve a design problem at a high enough level to recognize and use it over again. It's experience captured. The best description I have heard yet for recognizing a pattern is "design deja-vu." Ever been heads-down in a design and realized that you have faced a particular problem before? Were you able to solve it again using the same strategy? Well then you probably have a pattern in your design repertoire. So do your fellow softwaresmiths a favor and capture that experience.

Properly capturing and documenting a pattern is a difficult task. Fortunately, pattern gurus have defined a structured template for describing a pattern. Although variations exist, all descriptions reflect the same necessities to fully document a pattern. And pattern repositories exist on the internet for all of us to peruse and learn from.

A pattern example

Here is an abbreviated example of a documented pattern. This particular example is of the "Abstract Factory" pattern, one of the most easily recognizable and ubiquitous patterns in software development.

Name:

Abstract Factory

Also known as:

Factory, Kit, Creator

Motivation:

Often times it is necessary to hide the details of how an object is created from the clients that request the creation of that object. By placing the responsibility for creation of objects onto another object, the underlying creation mechanism can be tailored to specifics without impacting the object instigating the creation request and different kinds of objects can be created using the same consistent interface.

Applicability:

Use this pattern when the client making a creation request should be independent and ignorant of the underlying mechanism used to service the request.

Structure:

Diagram of system goes here.

Participants:

List all of the objects that participate in the pattern's implementation here.

Collaborations:

Client, Factory, Created Objects
Clients as the abstract factory for instances of created objects.
The abstract factory performs the work of actually creating the object instances and passing them to the client.
The created objects themselves are ignorant of this process.

Consequences:

The interface of the abstract factory is a point of immutability. Modifying it will affect all clients that use it.

Implementations:

Abstract Factories are often implemented as singletons.
Abstract Factories are often utilized in conjunction with the Proxy pattern to gain location transparency.
Abstract Factories are often used to hide the composition of an object from clients.

Sample Code:

Sample implementation of the pattern.

Known Uses:

COM Class Factories

Related Patterns:

Factory Method, Singleton

Even though this depiction is greatly abbreviated, it should be readily apparent that COM class factories are a classic example of this pattern. In fact, COM class factories utilize both the abstract factory pattern (the object itself) and the factory method pattern (the CreateInstance function). Regardless of how a pattern is documented, at a minimum the following key points are essential to any good pattern description:

  • The problem being addressed by the pattern
  • The context in which the problem occurs
  • The solution being provided
  • At lease one example of it's usage
  • Other names for the pattern
  • Relationships to other patterns (i.e. uses, is used by, relies on, etc.)

A pattern should be general enough to cover a diverse assortment of particular situations, yet just specific enough to firmly define the problem space being addressed. Perhaps the most of appealing aspect of patterns is their timelessness. In the constantly changing world of software, the permanent value of a good pattern is a unique and refreshing phenomenon.

Of course, no discussion of patterns would be complete without mentioning the classic text on the subject. "Design Patterns - Elements of Reusable Object-Oriented Software" by Gamma, Helm, Johnson, and Vlissides, aka "The Gang of Four". If you are interested in exploring patterns further, this book is a great place to start.

Patterns and COM

COM provides a fascinating and astute look at how patterns can be used to produce good software. COM is full of patterns, and examining how patterns are used within COM is an excellent way to gain insight into their use and power. In fact, COM and patterns provide an intriguing juxtaposition; they exist at opposite ends of the software reuse spectrum, Idea reuse vs. Binary reuse. As COM developers, we all value the idea of reusing our software at the binary level, but all levels of software reuse begin with a good design. And patterns help us fully realize the fruition of our work towards binary reuse.

Several of the classic "Gang of Four" patterns can be found throughout the world of COM development. Patterns within COM can be separated into two distinct varieties: those that are organic and integral to COM itself, and those that are created and used by resourceful COM developers. The "Bridge" pattern for example, which describes the notion of separating interface from implementation, is integral to COM. And the "Flyweight" pattern is a classic that has gained a new context of usage for people developing COM based software.

Two areas within COM where patterns are most evident are activation and composition. Focusing on activation, let's revisit the previous example of the abstract factory pattern to see how this powerful concept can be leveraged to solve a common COM design issue, the initialization of a COM object at creation time.

The COM ClassFactory

One of the great benefits of taking the time to examine COM from the perspective of patterns is that it often opens your eyes to solutions that already exist. Class Factories are a perfect example. By viewing the notion of a COM class factory as the realization of the abstract factory pattern coupled with the factory method pattern, we can begin to appreciate the true flexibility and strength of this idea. A lot of developers, both new to COM and not so new to COM, forget that a class factory is just another COM object. And that IClassFactory is just another interface. Contrary to seemingly popular belief, there is no law stating that all activation requests must be realized via CoCreateInstance. CoGetClassObject (among other methods) is a perfectly viable, and in many ways superior, alternative. By endowing your class factory with a custom interface, you can greatly increase activation flexibility for those clients willing or able to take advantage of it.

Let's take a look at a simple example:

Suppose we have a COM wrapper for the implementation of a network socket. It's easy to see that in order for this object to be of any use it's going to need, at a minimum, an IP address and port. And in order to avoid having too rigid an implementation, we want the client to be able to provide this information to the object. The trick here is getting this information from the client to the object. The initial instinct of the developer might be to place some sort of initialization routine on the object. Although this appears to be a simple solution, it raises some issues almost immediately. For starters, we have just placed semantic constraints upon our clients, which in turn places the responsibility for gracefully dealing with clients that don't follow those constraints upon ourselves. How do we handle the situation when our initialization routine is not called, or worse yet, called more than once? We could handle both situations by keeping some sort of flag internally that is checked at every crucial point within our code, but that quickly becomes ungainly at best. And what if our initialization routine is called properly and fails due to external forces? Should we require that our clients call Release() and then recreate a new object? Or should our implementation be constructed in such a way that initialization can be called again in this one special circumstance?

Design dйjа-vu anyone?

What we need to do is leverage the abstract factory and factory method patterns in a way that solves our particular situation. By creating our own custom class factory and giving it a different version of CreateInstance, we can provide clients a means to perform initialization at creation time and free our code from having to maintain unwieldy state and constraint enforcement logic.

Here is an IDL fragment that demonstrates a trivial example of a custom class factory interface:


interface ISocketFactory : IUnknown
{
HRESULT CreateSocketInstance( [in, unique] IUnknown *pOuter,
						[in] REFIID riid,
						[out, iid_is(riid)] void **ppv,
             [in] LONG lAddress,
             [in] LONG lPort );
};

Having that defined, we next need to modify the QueryInterface code for our class factory COM object. For demonstrative purposes I have presented these code examples using "raw" C++, for an example of implementing a custom class factory using ATL, see Richard Anderson's excellent article at the COMDeveloper web site.


STDMETHODIMP 
CSocketCoClass::QueryInterface(REFIID Iid, void **ppV)
{
      *ppV = 0;
      HRESULT hr(E_NOINTERFACE);
      
      // NOTE: As an option you may want to completely disable IClassFactory
      if((Iid == IID_IClassFactory) || (Iid == IID_IUnknown))
      {
             *ppV = static_cast<IClassFactory*>(this);
             reinterpret_cast<IUnknown*>(this)->AddRef();
             hr = S_OK;
      }
      else if(Iid == IID_ISocketFactory)
      {
            *ppV = static_cast<ISocketFactory*>(this);
             reinterpret_cast<IUnknown*>(this)->AddRef();
             hr = S_OK;
      }

    return hr;
}

And here is an implementation of . Which by the way, is an example of the Factory Method pattern.


STDMETHODIMP
CSocketCoClass::CreateSocketInstance ( IUnknown *pOuter,
			REFIID riid, void **ppv, LONG lAddress, LONG lPort )
{
      if (ppV == 0)
            return E_POINTER;
    
      *ppV = 0;

       if (pUnkOuter)
             return CLASS_E_NOAGGREGATION;
      
      if(!IsValidAddress(lAddress) || !IsValidPort(lPort))
            return E_INVALIDARG;

      // Create a new instance of the CSocketObj C++ class.
      CSocketObj *pObj = new CSocketObj(lAddress, lPort);

     // If the new operation fails, return E_OUTOFMEMORY.
     if (!pObj)
           return E_OUTOFMEMORY;

     // Call AddRef on the new object to stabilize its reference count.
     pObj->AddRef();

     // QI the new object to fetch an interface pointer for the caller.
     HRESULT hr = pObj->QueryInterface(Iid, ppV);
      
     // Call Release on the new object. If QueryInterface failed.
     pObj->Release();

     // Return the results of the call to QueryInterface.
     return hr;
}

As you can see, this custom factory method is almost identical to the traditional IClassFactory::CreateInstance method. The key here is the call to the CSocketObj constructor made by the class factory. The role of an abstract factory is to insulate clients from the details of the creation mechanism. Factory objects allow for a point of intimacy within the implementation code that is isolated from the outside world. The interface implemented by CSocketObj no longer requires any kind of initialization method; the fact that an instance of the class exists implies initialization. And the implementation code is not burdened with awkward state and logic to protect itself from belligerent clients. Granted, there are more robust ways of getting this done besides arguments to a constructor, the point is that no matter how you perform your initialization, it takes place at a level below the interface. The factory allows you to safely manipulate the objects that it creates with a degree of intimacy that you would never want to expose to the cold, cruel world.

This is a powerful concept. And a pattern for design reuse that is proven to be valid in a large number of situations.

Finally, here is a code fragment demonstrating activation by a client:


ISocketFactory *pFact = 0;

HRESULT hr(CoGetClassObject(CLSID_CoSocketFactory, 
			CLSCTX_INPROC_SERVER, 0, IID_ISocketFactory,
			(void**)&pFact));
if(SUCCEEDED(hr))
{
      LONG lAddress(0x371400E1); // 225.0.20.55
      LONG lPort(5050);
      ISocketObj *pSockObj(0);
      
      hr = pFact->CreateSocketInstance(0, IID_ISocketObj,
	  		(void**)&pSockObj, lAddress, lPort);
      if(SUCCEEDED(hr))
     {
          // Do Something Cool Here
          pSockObj->Release();
     }
     
     pFact->Release();
}

The client now replaces what would be a call to CoCreateInstance with a call to CoGetClassObject followed by a call to QueryInterface. And then uses the extended capabilities of the class factory through the provided ISocketFactory interface.

In Summary

Patterns are powerful design concepts that allow us to recognize and solve recurring problems within a software system. COM is an excellent example of how patterns can be used to produce flexible and adaptable software. By examining the patterns that are organic to COM we can gain insight into how to solve design issues that we encounter. Using patterns when designing software within COM based systems can lead to more effective code reuse at the binary level.


What do you think of this article?

Have your say about the article. You can make your point about the article by mailing [email protected] (If you haven't allready joined, you can join by going to onelist.com/community/dev-com).

You can also write a review. We will publish the best ones here on this article. Send your review to [email protected].

Mail a question to the author!!

As part of the IDevResource commitment to Open Publishing, all of our authors are available to answer all of your trickiest questions at Author Central. For information about the authors, or to mail a question, visit them at Author Central.


iDevJobs.com - Jobs for Professional Developers

Contribute to IDR:

To contribute an article to IDR, a click here.

To contact us at IDevResource.com, use our feedback form, or email us.

To comment on the site contact our webmaster.

Promoted by CyberSavvy UK - website promotion experts

All content © Copyright 2000 IDevResource.com, Disclaimer notice

Visit the IDR Forums

WTL Introduction

Visit the IDR Bookstore!



Learn C#