SDSU CS 635: Advanced Object-Oriented Design & Programming
Spring Semester, 1998
Creational Patterns -Factory Method, Prototype, Abstract Factory

To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Apr-98

Contents of Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory


Reference slide # 1
Factory Method slide # 2
......Maze Class Version 1 slide # 3
...Implementation slide # 7
......Parameterized Factory Methods slide # 8
......C++ Templates to Avoid Subclassing slide # 8
......Java forName and Factory methods slide # 9
......Clients Can Use Factory Methods slide # 10
Prototype slide # 12
...Intent slide # 12
...Applicability slide # 12
...Implementation/Sample Code slide # 13
...Cloning Issues slide # 14
......How to in C++ - Copy Constructors slide # 14
......How to in Java - Object clone() slide # 15
......Shallow Copy Verse Deep Copy slide # 16
...Example of Prototype slide # 18
...Consequences slide # 27
...Implementation Issues slide # 27
Abstract Factory slide # 28
...How Do Factories create Widgets slide # 32
......Method 2) Their Factory Method slide # 33
......Method 3) Prototype slide # 35
...Applicability slide # 36
...Consequences slide # 36

Reference

Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995, chapter 3

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 2

Factory Method

Example - Maze Game


Classes for Mazes

Now a maze game has to make a maze, so we might have something like:


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 3

Maze Class Version 1

class MazeGame
     {
     
     public Maze createMaze()
          {
          Maze aMaze = new Maze();
          
          Room r1 = new Room( 1 );
          Room r2 = new Room( 2 );
          Door theDoor = new Door( r1, r2);
          
          aMaze.addRoom( r1 );
          aMaze.addRoom( r2 );
          
          r1.setSide( North, new Wall() );
          r1.setSide( East, theDoor );
          r1.setSide( South, new Wall() );
          r1.setSide( West, new Wall() );

          r2.setSide( North, new Wall() );
          r2.setSide( East, new Wall() );
          r2.setSide( South, new Wall() );
          r2.setSide( West, theDoor );
          
          return aMaze;
          }
     }


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 4
How do we make Other Mazes?
Idea 1 - Subclass MazeGame, override createMaze




class BombedMazeGame extends MazeGame
     {
     
     public Maze createMaze()
          {
          Maze aMaze = new Maze();
          
          Room r1 = new RoomWithABomb( 1 );
          Room r2 = new RoomWithABomb( 2 );
          Door theDoor = new Door( r1, r2);
          
          aMaze.addRoom( r1 );
          aMaze.addRoom( r2 );
          
          r1.setSide( North, new BombedWall() );
          r1.setSide( East, theDoor );
          r1.setSide( South, new BombedWall() );
          r1.setSide( West, new BombedWall() );

etc.

Note the amount of cut and paste!


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 5
How do we make Other Mazes?
Idea 2 - Factory Method
class MazeGame
     {
     
     public Maze makeMaze() { return new Maze(); }
     public Room makeRoom(int n ) { return new Room( n ); }
     public Wall makeWall() { return new Wall(); }
     public Door makeDoor() { return new Door(); }
     
     public Maze CreateMaze()
          {
          Maze aMaze = makeMaze();
          
          Room r1 = makeRoom( 1 );
          Room r2 = makeRoom( 2 );
          Door theDoor = makeDoor( r1, r2);
          
          aMaze.addRoom( r1 );
          aMaze.addRoom( r2 );
          
          r1.setSide( North, makeWall() );
          r1.setSide( East, theDoor );
          r1.setSide( South, makeWall() );
          r1.setSide( West, makeWall() );

          r2.setSide( North, makeWall() );
          r2.setSide( East, makeWall() );
          r2.setSide( South, makeWall() );
          r2.setSide( West, theDoor );
          
          return aMaze;
          }
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 6
Now subclass MazeGame override make methods

CreateMaze method stays the same


class BombedMazeGame extends MazeGame
     {
     
     public Room makeRoom(int n ) 
          { 
          return new RoomWithABomb( n ); 
          }

     public Wall makeWall() 
          { 
          return new BombedWall(); 
          }


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 7

Implementation


Top level Factory method is in an abstract class

abstract class MazeGame
     {
     
     public Maze makeMaze();
     public Room makeRoom(int n );
     public Wall makeWall();
     public Door makeDoor();

class MazeGame
     {
     
     public:
          virtual Maze* makeMaze() = 0;
          virtual Room* makeRoom(int n ) = 0;
          virtual Wall* makeWall() = 0;
          virtual Door* makeDoor() = 0;

Top level Factory method is in a concrete class

"Create objects in a separate operation so that subclasses can override the way they're created"


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 8

Parameterized Factory Methods


class Hershey 
     {
     
     public Candy makeChocolateStuff( CandyType id )
          {
          if ( id == MarsBars ) return new MarsBars();
          if ( id == M&Ms ) return new M&Ms();
          if ( id == SpecialRich ) return new SpecialRich();
          
          return new PureChocolate();
          }

C++ Templates to Avoid Subclassing


template <class ChocolateType>
class Hershey 
     {
     public:
          virtual Candy* makeChocolateStuff( );
     }

template <class ChocolateType>
Candy* Hershey<ChocolateType>::makeChocolateStuff( )
     {
     return new ChocolateType;
     }

Hershey<SpecialRich> theBest;

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 9

Java forName and Factory methods


class Hershey 
     {
     
     private String chocolateType;

     public Hershey( String chocolate )
          {
          chocolateType = chocolate;
          }

     public Candy makeChocolateStuff( )
          {
          Class candyClass =  Class.forName( chocolateType );
          return (Candy) candyClass.newInstance();          
          }

Hershey theBest = new Heshsey( "SpecialRich" );


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 10

Clients Can Use Factory Methods


class CandyStore
     {
     Hershey supplier;
     public restock()
          {
          blah

          if ( chocolateStock.amount() < 10 )
               {
               chocolateStock.add( 
                    supplier.makeChocolateStuff() );
               }

     blah


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 11
How do we make Other Mazes?
Idea 3 - Factory Method in Client or is that Prototype?

class Room extends MapSite
     {
     public Room makeRoom()  { return new Room(); }
     etc,
     }

class RoomWithBomb extends Room
     {
     public Room makeRoom()  { return new RoomWithBomb(); }
     etc,
     }

etc. 

class MazeGame
     {
     public Room roomMaker; 
     etc.
     
     public MazeGame( Room rfactory  )
          {
          roomMaker = rfactory;
          }

     public Maze CreateMaze()
          {
          Maze aMaze = new Maze();
          
          Room r1 = roomMaker.makeRoom( 1 );
          etc.


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 12

Prototype

Intent


Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype


Applicability


Use the Prototype pattern when a system should be independent of how its products are created, composed, and represented; and


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 13

Implementation/Sample Code

Simple Example

class Prototype
     {
     public Prototype clone() 
          {
          code to make a copy of current Prototype object
          return clone;
          }

          // add what ever else you want the class to do
     }


class Protoplasm extends  Prototype
     {
     public Prototype clone() 
          {
          code to make a copy of current Protoplasm object
          return clone;
          }

     // add more other stuff
     }

ClientCodeMethod( Prototype example )
     {
     Prototype myCopy = example.clone();

     // do some work using myCopy
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 14

Cloning Issues

How to in C++ - Copy Constructors


class Door 
     {
     public:
          Door();
          Door( const Door&);

          virtual Door* clone() const;

          virtual void Initialize( Room*, Room* );
          // stuff not shown
     private:
          Room* room1;
          Room* room2;
     }

Door::Door ( const Door& other ) //Copy constructor
     {
     room1 = other.room1;
     room2 = other.room2;
     }

Door* Door::clone()  const 
     {
     return new Door( *this );
     }


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 15

How to in Java - Object clone()


protected Object clone() throws CloneNotSupportedException

Creates a clone of the object. A new instance is allocated and a bitwise clone of the current object is place in the new object.

Returns:

a clone of this Object.

Throws: OutOfMemoryError
If there is not enough memory.

Throws: CloneNotSupportedException
Object explicitly does not want to be cloned, or it does not support the Cloneable interface.

class Door implements Cloneable 
     {
     public void Initialize( Room a, Room b) 
          { room1 = a; room2 = b; }

     public Object clone() throws
                          CloneNotSupportedException
          {
          return super.clone();
          }

     Room room1;
     Room room2;
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 16

Shallow Copy Verse Deep Copy


Original Objects

door pic


Shallow Copy

shallowCopy


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 17
Shallow Copy Verse Deep Copy

Original Objects

door pic


Deep Copy

deep copy


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 18

Example of Prototype



JavaServer handles the network connection.

Algorithm:

Wait for client to connect
Open connection
Create IO streams to client
Create Server Engine to handle request
Let engine handle the request

Variations:

JavaServer will want to multiple copies of server engine to handle more than one client at a time

Would like to write JavaServer just once and use it for all possible different server engines

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 19
JavaSever With Factory Method
For a DateServer


class JavaServer
     {
     ServerSocket acceptor;
     
     public JavaServer( int portNumber ) 
          {
          acceptor = new ServerSocket( portNumber );
          }

     public void run()
          {
          while (true)
               {
               Socket client = acceptor.accept();
               InputStream cin = client.getInputStream();
               OutputStream cout = client.getOutputStream();
               processClientRequest( cin, cout );
               }
          }

     private void processClientRequest( InputStream cin,
                                         OutputStream cout )
          {
          DateServer  handler = new DateServer( cin, cout);
          handler.processClientRequest();
          }
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 20
JavaServer with Template Method

class AirlineReservationJavaServer extends JavaServer
     {     
     public AirlineReservationServer( int portNumber ) 
          {
          super( portNumber );
          }


     private void processClientRequest( InputStream cin,
                                         OutputStream cout )

          AirlineReservation  handler;
          handler = new AirlineReservation( cin, cout );
          handler.processClientRequest();
          }
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 21
Design Principle 1

Program to an interface, not an implementation

Use abstract classes (and/or interfaces in Java) to define common interfaces for a set of classes

Declare variables to be instances of the abstract class not instances of particular classes


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 22
The Interface

interface ServerEngine
     {
     public void processClientRequest(InputStream in, 
                                        OutputStream out);
     }





Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 23
JavaServer with Factory Method

abstract class JavaServer
     {
     ServerSocket acceptor;
     
     public JavaServer( int portNumber ) 
          {
          acceptor = new ServerSocket( portNumber );
          }

     public void run()
          {
          while (true)
               {
               Socket client = acceptor.accept();
               InputStream cin = client.getInputStream();
               OutputStream cout = client.getOutputStream();
               ServerEngine requestHandler =
                    makeServerEngine();

               requestHandler.processClientRequest( cin, cout );
               }
          }

     abstract protected ServerEngine makeServerEngine();
     }


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 24

class AirlineReservationJavaServer extends JavaServer
     {     
     public AirlineReservationServer( int portNumber ) 
          {
          super( portNumber );
          }

     protected ServerEngine makeServerEngine()
          {
          return new AirlineReservation( );
          }
     }

class AirlineReservation implements ServerEngine
     {
     public void processClientRequest(InputStream in, 
                                        OutputStream out)
          {
          blah
          }
     etc.
     }


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 25
JavaSever With Interface and Prototype
For Any Server

interface ServerEngine
     {
     public ServerEngine clone( InputStream in, 
                                        OutputStream out );

     public void processClientRequest();
     }

class JavaServer
     {
     ServerSocket acceptor;
     ServerEngine serverPrototype;
     
     public JavaServer( int portNumber, ServerEngine aCopy) 
          {
          acceptor = new ServerSocket( portNumber );
          serverPrototype = aCopy;
          }

     public void run() {
          while (true)
               {
               Socket client = acceptor.accept();
               InputStream cin = client.getInputStream();
               OutputStream cout = client.getOutputStream();
               ServerEngine requestHandler =
                    serverPrototype.clone( cin, cout);

               requestHandler.processClientRequest( cin, cout );
               }
          }
     }
Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 26
Driver Program

class DriverProgram
     {
     public  static  void  main( String  args[] )
          {
          ServerEngine aPrototype = new DateServer();
          JavaServer  networkListener;
          networkListener = new JavaServer( 6666,  aPrototype );
          networkListener.run();
          }
     }

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 27

Consequences



Implementation Issues



Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 28

Abstract Factory

Example - Write a Cross Platform Window Toolkit

Write a toolkit to allow applications with GUI interfaces to run on multiple platforms (Mac, PC, OS/2, Unix motif ) using the look and feel of each platform

We will look at widgets: Windows, Menu's and Buttons

Create an interface( or abstract class) for each widget and an concrete class for each platform:



Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 29
This allows the application to write to the widget interface

public void installDisneyMenu()
     {
     Menu disney = create a menu somehow
     disney.addItem( "Disney World" );
     disney.addItem( "Donald Duck" );
     disney.addItem( "Mickey Mouse" );
     disney.addGrayBar( );
     disney.addItem( "Minnie Mouse" );
     disney.addItem( "Pluto" );
     etc.
     }

How to create the widget?


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 30
Use Abstract Factory

abstract class WidgetFactory
     {
     public Window createWindow();
     public Menu createMenu();
     public Button createButton();
     }
     
class MacWidgetFactory extends WidgetFactory
     {
     public Window createWindow() 
          { code to create a mac window }
          
     public Menu createMenu() 
          { code to create a mac Menu }
          
     public Button createButton() 
          { code to create a mac button }
     }     

class Win95WidgetFactory extends WidgetFactory
     {
     public Window createWindow() 
          { code to create a Win95 window }
          
     public Menu createMenu() 
          { code to create a Win95 Menu }
          
     public Button createButton() 
          { code to create a Win95 button }
     }
Etc.

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 31
Now to get code that works for all platforms we get:

public void installDisneyMenu(WidgetFactory myFactory)
     {
     Menu disney = myFactory.createMenu();
     disney.addItem( "Disney World" );
     disney.addItem( "Donald Duck" );
     disney.addItem( "Mickey Mouse" );
     disney.addGrayBar( );
     disney.addItem( "Minnie Mouse" );
     disney.addItem( "Pluto" );
     etc.
     }

We just need to make sure that the application for each platform creates the proper factory

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 32

How Do Factories create Widgets?

Method 1) My Factory Method


abstract class WidgetFactory
     {
     public Window createWindow();
     public Menu createMenu();
     public Button createButton();
     }
     
class MacWidgetFactory extends WidgetFactory
     {
     public Window createWindow() 
          { return new MacWidow() }
          
     public Menu createMenu() 
          { return new MacMenu() }
          
     public Button createButton() 
          { return new MacButton()  }
     }     


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 33
How Do Factories create Widgets?

Method 2) Their Factory Method


abstract class WidgetFactory
     {
     private Window windowFactory;
     private Menu menuFactory;
     private Button buttonFactory;

     public Window createWindow()
          { return windowFactory.createWindow() }

     public Menu createMenu();
          { return menuFactory.createWindow() }

     public Button createButton()
          { return buttonFactory.createWindow() }

     }
     
class MacWidgetFactory extends WidgetFactory
     {
     public MacWidgetFactory()
          {
          windowFactory = new MacWindow();
          menuFactory = new MacMenu();
          buttonFactory = new MacButton();
          }
     }     

class MacWindow extends Window
     {
     public Window createWindow() { blah }
     etc.

Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 34
How Do Factories create Widgets?
Method 2) Their Factory Method
When does this make Sense?

There might be more than one way to create a widget

abstract class WidgetFactory
     {
     private Window windowFactory;
     private Menu menuFactory;
     private Button buttonFactory;

     public Window createWindow()
          { return windowFactory.createWindow() }

     public Window createWindow( Rectangle size)
          { return windowFactory.createWindow( size ) }

     public Window createWindow( Rectangle size, String title)
          { return windowFactory.createWindow( size, title) }

     public Window createFancyWindow()
          { return windowFactory.createFancyWindow() }

     public Window createPlainWindow()
          { return windowFactory.createPlainWindow() }

Using factory method allows abstract class to do all the different ways to create a window.

Subclasses just provide the objects windowFactory, menuFactory, buttonFactory, etc.


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 35
How Do Factories create Widgets?

Method 3) Prototype


class WidgetFactory
     {
     private Window windowPrototype;
     private Menu menuPrototype;
     private Button buttonPrototype;

     public WidgetFactory( Window windowPrototype,
                              Menu menuPrototype,
                              Button buttonPrototype)
          {
          this.windowPrototype = windowPrototype;
          this.menuPrototype = menuPrototype;
          this.buttonPrototype = buttonPrototype;
          }

     public Window createWindow()
          { return windowFactory.createWindow() }

     public Window createWindow( Rectangle size)
          { return windowFactory.createWindow( size ) }

     public Window createWindow( Rectangle size, String title)
          { return windowFactory.createWindow( size, title) }

     public Window createFancyWindow()
          { return windowFactory.createFancyWindow() }

etc.

There is no need for subclasses of WidgetFactory.


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 36

Applicability



Consequences


Implementation


Doc 12, Creational Patterns -Factory Method, Prototype, Abstract Factory Slide # 37
Problem: Cheating Application Code

public void installDisneyMenu(WidgetFactory myFactory)
     {
     // We ship next week, I can't get the stupid generic Menu to 
     // do the fancy Mac menu stuff
     // Windows version won't ship for 6 months
     // Will fix this later

     MacMenu disney = (MacMenu) myFactory.createMenu();
     disney.addItem( "Disney World" );
     disney.addItem( "Donald Duck" );
     disney.addItem( "Mickey Mouse" );
     disney.addMacGrayBar( );
     disney.addItem( "Minnie Mouse" );
     disney.addItem( "Pluto" );
     etc.
     }

visitors since 03-Mar-98