Creational

Prototype

Purpose

Use a prototypical instance to specify the kinds of objects to create. New objects are created by cloning he prototypical instance.

Note: Prototype and Abstract Factory are competing patterns. 

UML

Behavior

A client creates the required object by asking the prototype to clone itself. The Clone() is the operation by which the prototypical instance can create the required object. Clone() does create a new object, however, the new object is a copy of the prototypical instance.

Example

The example is the same as in Abstract Factory. Note the following mapping:

Abstract Factory Prototype
Concrete Factory

Client

Abstract Product Prototype
Concrete Product Concrete Prototype

The following example uses the Prototype pattern to allow an application to be dynamically configured with a UI skin. The UIFactory class is the client that creates a new widget by asking a prototype instance to clone itself. Whether we wanted to use Windows UI widgets or Java UI widgets, all we have to do is initialize the Client with the appropriate base prototype class. Methods on the Client use the prototype instance to create the appropriate widgets.

Usage

/* Prototype Class*/

/* Prototype: Declares an interface for a cloning itself In this example, the Prototype declares an interface for a specific widget, a button */
class CButton
{
// Constructors/Destructor
public:
    virtual ~CButton() {}

// public Interface
public:
    virtual CButton* Clone() = 0;

    // Methods for interfacing with a button ...
    virtual void Click() = 0;
    virtual void Enable( bool bEnabled ) = 0;
};

/* ConcretePrototype: Implements an operation for cloning itself. */
class CWindowsButton : public CButton
{
public:
// Constructors/Destructor
    CWindowsButton(){}
    CWindowsButton( const CWindowsButton& src) { /* Deep copy data members */ }
public:
    CButton* Clone()             { return new CWindowsButton( *this ); }
    void Click()                 { std::cout << "CWindowsButton::Click" << std::endl;}
    void Enable( bool bEnabled ) { std::cout << "CWindowsButton::Enable" << std::endl;}
};

class CJavaButton : public CButton
{
public:
// Constructors/Destructor
    CJavaButton(){}
    CJavaButton( const CJavaButton& src) { /* Deep copy data members */ }
public:
    CButton* Clone()             { return new CJavaButton( *this ); }
    void Click()                 { std::cout << "CJavaButton::Click" << std::endl;}
    void Enable( bool bEnabled ) { std::cout << "CJavaButton::Enable" << std::endl;}
};

/* Prototype Class*/

/* Prototype: Declares an interface for a cloning itself In this example, the Prototype declares an interface for a specific widget, a window */
class CWindow
{
// Constructors/Destructor
public:
    virtual ~CWindow() {}

// public Interface
public:
    virtual CWindow* Clone() = 0;
    virtual void SetStyle( int nStyle ) = 0;
    virtual void SetSize( int nLength, int nHeight) = 0;

    // ... Other methods for interfacing with a window
};

/* ConcretePrototype: Implements an operation for cloning itself. */
class CWindowsWindow : public CWindow
{
public:
// Constructors/Destructor
    CWindowsWindow(){}
    CWindowsWindow( const CWindowsWindow& src) { /* Deep copy data members */ }

// public Interface
public:
    CWindow* Clone()                 { return new CWindowsWindow( *this ); }
    void SetStyle( int nStyle )      { std::cout << "CWindowsWindow::SetStyle" << std::endl;}
    void SetSize( int nLen, int nHt) { std::cout << "CWindowsWindow::SetSize" << std::endl;}
};

class CJavaWindow : public CWindow
{
public:
// Constructors/Destructor
    CJavaWindow(){}
    CJavaWindow( const CJavaWindow& src) { /* Deep copy data members */ }

public:
    CWindow* Clone()                 { return new CJavaWindow( *this ); }
    void SetStyle( int nStyle )      { std::cout << "CJavaWindow::SetStyle" << std::endl;}
    void SetSize( int nLen, int nHt) { std::cout << "CJavaWindow::SetSize" << std::endl;}
};

/* Client */

/* Client class: Creates the requires object by asking prototype instance to clone itself */
class UIFactory
{
// Constructors/Destructor
public:
    UIFactory( CWindow* pW, CButton* pB ) : 
        m_pPrototypeWindow(pW), m_pPrototypeButton(pB) {}

    ~UIFactory()
    {
        delete m_pPrototypeWindow;
        delete m_pPrototypeButton;
    }

// Public Interface
    virtual CWindow* CreateWindow() { return m_pPrototypeWindow->Clone(); }
    virtual CButton* CreateButton() { return m_pPrototypeButton->Clone(); }

// Data members
private:
    CWindow* m_pPrototypeWindow;
    CButton* m_pPrototypeButton; 
};

// Helpers
void CreateNewLookAndFeel( const std::auto_ptr<UIFactory>& spFactory );
int  GetUserUIChoice();

int main(int argc, char* argv[])
{
    std::auto_ptr<UIFactory> spFactory;
    switch( GetUserUIChoice() )
    {
     case 10: // Windows UI
        spFactory = std::auto_ptr<UIFactory>( new UIFactory(new CWindowsWindow, new CWindowsButton) );
        CreateNewLookAndFeel( spFactory );
        break;
     case 11: // Java UI
        spFactory = std::auto_ptr<UIFactory>( new UIFactory(new CJavaWindow, new CJavaButton) );
        CreateNewLookAndFeel( spFactory );
    } 
    return 0;
}

/* Simulate user interaction by returning an integer denoting uer choice */
int GetUserUIChoice() { return 10; // Windows UI }

/* CreateNewLookAndFeel simulates creating and adding widgets to the screen. Note how creating different UIs uses the same code base */
void CreateNewLookAndFeel( const std::auto_ptr<UIFactory>& spFactory )
{
    /* Create a window. The window can then be added to screen */
    std::auto_ptr<CWindow> spWin( spFactory->CreateWindow() );
    spWin->SetSize( 10, 20);
    spWin->SetStyle( 0 );

    /* Create a Window button. Button can then be added to a window*/
    std::auto_ptr<CButton> spBtn (spFactory->CreateButton() );
    spBtn->Enable( true );
}

Notes