See also: Design pattern, Abstract factory pattern, ClassFactory
from mode-x.com (contributed by author):
The Factory pattern is useful to solve a common problem: Very often you'll have to construct an object, based on some input, and the functions inside the object will depend upon the input. A good example would be a class to read image files and make thumbnails out of them. You could, of course, bung them all into one class and have it decide which bit of code to run on its own:
Factory design pattern in Java
public class ImageReader
{
private int FileType;
private String FileContents;
private byte[] DecodedImage;
public ImageReader( InputStream in )
{
// Figure out what type of file this input stream represents
// (eg gif, jpeg, png, tif, etc )
FileType = file_type;
decodeFile();
}
private decodeFile()
{
switch( FileType )
{
case ImageReader.GIF:
// do gif decoding (many lines)
break;
case ImageReader.JPEG:
// do jpeg decoding (many lines)
break;
case ImageReader.PNG:
// do png decoding (many lines)
break;
// etc...
}
}
}
1 - A bad way to design the ImageReader
This method has the advantage of abstracting the file type from the class that calls this ImageReader, but as the number of file types supported gets larger, the code will quickly become huge, unwieldy and hard to maintain.
Another solution that seems possible is to have a separate object for each of these file types:
public Interface ImageReader { public ImageReader( InputStream in ); public getDecodedImage(); } public class GifReader implements ImageReader { public GifReader( InputStream in ) { // check that it's a gif, throw exception if it's not, then if it is // decode it. } public getDecodedImage() { return DecodedImage; } } public class JpegReader implements ImageReader { /.... } // Then you would use them as: public class MyProg { public static void main( String[] args ) { String filename = args[0]; ImageReader out; if( endsIndotgif( filename ) ) { out = (ImageReader)new GifReader( file_input_stream ); } if( endsIndotjpeg( filename ) ) { out = (ImageReader)new JpegReader( file_input_stream ); } printOut( out.getDecodedImage ); } } 2 - Another better, but still not so good way to read imagesAgain, there are advantages to this method (clean organisation of the ImageReader classes, split up in several classes), but this is at the cost of the abstraction of the image type. Again, when you get to have dozens of file types, this will be unsatisfactory and produce code that is hard to maintain.
So what's the solution? Simply to take the best of both worlds, by using the Factory pattern (which you should already have an idea of by now). The Factory is a class that returns another class depending on the context. So in this situation, keeping the separate class design as in the last example:
public class ImageReaderFactory { public static ImageReader getImageReader( InputStream is ) { int ImageType = figureOutImageType( is ); switch( ImageType ) { case ImageReaderFactory.GIF: GifReader r = new GifReader( is ); return( (ImageReader)r ); break; case ImageReaderFactory.JPEG: JpegReader r = new JpegReader( is ); return( (ImageReader)r ); break; // etc. } } } 3 - A good design for the ImageReader: The Factory design patternEt voilą! That's all there is to it. The Factory pattern, while not so complex as to be awe-inspiring, is a neat solution to a problem that occurs fairly often. Next time you see a situation where you need a different object depending on the context, you'll think of the Factory pattern.
The combination of a type code and its associated specific factory object can be stored in a map. The switch statement can be avoided to create an extensible factory by a mapping.