Structural

Decorator

Purpose

Dynamically Attach/unattach additional functionalities to an object. 

UML

Behavior

Assume a decorator object adds a background image to a text document. 



The decorator object encloses the document it wishes to decorate. And since the decorator conforms to the interface of the component (i.e., document) it encloses, clients work with the decorator as if they were working with the document. The decorator is transparent, meaning that clients cannot tell the difference between a decorated object and an undecorated one: When the client calls on the decorator, it forwards requests to the component it encloses and performs any additional activities, such as painting the background.

Example

The decorator pattern is used to attach compressing functionality to file streams.

Usage

/* Component Classes */

/* A base class defining the interface for objects that can have additional responsibilities added to them dynamically */

class CStream 
{
// constructors/destructors
public:
    CStream() {}
    virtual ~CStream() {}

// public interface
    virtual void WriteToStream(int n)
    {
        // Default implementation comes here
    }
};

/* Defines an object to which additional responsibilities are attached dynamically. It inherits its interface from CStream */
class CFileStream : public CStream
{
// constructors/destructors
public:
    CFileStream(){}
    virtual ~CFileStream(){}

// public interface
public:
    virtual void WriteToStream(int n)
    {
        // FileStream-sepcific implementation of WriteToStream() comes here
    }
};

/* Decorator Classes */

/* Dynamically decorates object derived from CStream with additional responsibilities by defining an interface that conforms to the decorated object, CStream. Client using CStream-derived classes, i.e., CFileStream can instead use CDecorator. class transparently since CDecorator maintains a reference to a CStream object */

class CStreamDecorator : public CStream 
{
// constructors/destructors
public:
    CStreamDecorator( CStream *pStream = NULL) : m_pStream( pStream ){}
    virtual ~CStreamDecorator(){};     // virtual because this class will be a base class

// public interface
public:
    // Must define same public interface methods so that clients can use a decorator
    // object transparently (as if the client was using a CStream-derived object)
    virtual void WriteToStream(int n)
    {
        // Assert m_pStream is valid ...

        // Delegate to the original object by calling WriteToStream on the dynamic
        // type of m_pStream (in the client, the dynamic type is CFileStream)
        m_pStream->WriteToStream( n );
    }

protected:
    // m_pStream is protected so that a CStreamDecorator class can assess it
    // and perform the required 'decoration' on it.
    CStream *m_pStream;
};

/* The decorator that adds specific responsibilities to CStream-derived objects */
class CUpperCaseStreamDecorator : public CStreamDecorator
{
// Constructors/Destructors
public:
    CUpperCaseStreamDecorator( CStream *pStream = NULL) : CStreamDecorator(pStream) {}
    virtual ~CUpperCaseStreamDecorator() {}

// public interface
    // Must define same public interface methods so that clients can use a decorator
    // object transparently (as if the client was using a CStream-derived object)
    virtual void WriteToStream(int n)
    {
        // Delegate to the original object
        CStreamDecorator::WriteToStream( n );

        // Most importantly, perform the work required by this decorator
        ToUpperCase( );
    }

private:
    void ToUpperCase()
    { 
        // Access and manipulate m_pStream here (recall it's protected in the base
        // class so that a derived class can access it.)
        /* SomeHelperFunction(m_pStream); */
    }
};

int main(int argc, char* argv[])
{

    /* Create a CFileStream object
    Recall: Static type is CStream. Dynamic type is CFileStream */
    CStream *pStream = new CFileStream;

    /* Dynamically attach additional responsibilities (i.e., extra method) by
    invoking a method on the CFileStream decorator. 
    Note how this client uses pUCSD as if it was using a CFileStream object. */
    CStreamDecorator *pUCSD = new CUpperCaseStreamDecorator( pStream );
    pUCSD->WriteToStream( 2001 );

    // Clean up
    delete pStream;
    delete pUCSD;

    return 0;
}

Notes