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.
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.
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.
/* 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 );
}