Use the Factory Pattern to Facilitate Dynamic Typing

ood old object-oriented programming is still alive and kicking. In many C++ frameworks and applications where inheritance and virtual functions thrive you often need to create a derived object whose precise type can only be determined at runtime. Lean how Factory can boost your code’s reliability and performance.


Your application has to create a derived object but the exact type of that object isn’t known at compile-time. How do you implement a generic mechanism that creates the right type of object at runtime?


Use the Factory design pattern.

Spitting Images

Suppose you have an abstract class from which concrete classes are derived:

class Image //abstract{public: virtual void image_type() const=0; virtual ~Image()=default; //...};class JpgImage: public Image{public: virtual void image_type() const {cout<<"jpg image"<

The only feature that needs to be explained is the =default specifier in the base class' destructor. Put simply, this specifier rids you of the burden (and potential performance overhead) of defining a manual empty destructor for the abstract base class, while still ensuring that the destructor is virtual (read more on defaulted functions here).

Next, assume that you're designing an image viewer that handles various image formats: .bmp, .jpeg, .png, and so on. The viewer can determine only at runtime which derived object it needs in order to display and edit the image that the user has selected. To do that, the viewer needs an engine (known as a factory) that creates the right type of object according to the file extension. For example, when the user clicks on an image with the .jpg extension the factory will create a JpgImageobject to handle this format.

Factory Class

Factory is implemented as an independent class with a static member function called create(). create() returns a pointer whose static type is that of the abstract base class. The dynamic typeof the returned pointer will reflect the actual object that has been created for viewing the image:

class ImageFactory {public:  static Image* create(const std::string& ext)   {      if (ext=="jpg")         return new JpgImage;      else if (ext=="bmp")         return new BmpIamge;      else //default          return new PngImage;  }};

Usage

Technically, you don't need to instantiate an ImageFactory object to call create() because it's a static member function. However, for the sake of an intuitive and cleaner syntax, you can still create a factory object, as shown in the following code listing. Additionally, it's best to use a smart pointer to ensure automatic cleanup of object created by the factory class. Smart pointers such as std::tr1::shared_ptr, std::auto_ptr, and its successor, std::unique_ptr, also offer the convenience of the member function reset()which disposes of an image object and binds a new object to the same smart pointer in one shot:

int main(){  ImageFactory factory;  std::auto_ptr img(factory.create("jpg"));  img->image_type(); //output: "jpg image"  img.reset(factory.create("bmp"));  img->image_type();//output: "bmp image"  img.reset(factory.create("")); //default  img->image_type(); //output: "png image"}

Factory Refactored

Although the orthodox Factory pattern uses string comparisons to determine the actual derived object, there is a cleaner and more efficient way to achieve the same results. Replacing the string argument of create() with an enumeration will produce significantly faster code and enable you to use a switch which is more readable and easier to maintain than a list of else-ifstatements.

First, add an enumeration into the Image Factory class:

class ImageFactory{public:  enum ImgFormat   {    JPG,    BMP,    //...add new image formats as necessary    DEFAULT   };};

Next, add a switch block to create():

 static Image* create(ImgFormat fmt){  switch (fmt)  {   case JPG:     return new JpegImage;     break;   case  BMP:     return new BmpImage;     break;   default:     return new PngImage;  }}

Finally, change main()accordingly:

int main(){ ImageFactory factory; auto_ptr img(factory.create(ImgFactory::JPG)); img->image_type(); img.reset(factory.create(ImageFactory::BMP)); img->image_type(); img.reset(factory.create(ImageFactory::DEFAULT));  img->image_type();}

A Timeless Solution

Recently, I've seen several questions in the C++ forumregarding techniques for creating derived objects at runtime. In all these cases, Factory has proved to be the right solution. This only goes to show that classic design patterns are still as relevant as ever.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

microsoft careers

Top Careers at Microsoft

Microsoft has gained its position as one of the top companies in the world, and Microsoft careers are flourishing. This multinational company is efficiently developing popular software and computers with other consumer electronics. It is a dream come true for so many people to acquire a high paid, high-prestige job

your company's audio

4 Areas of Your Company Where Your Audio Really Matters

Your company probably relies on audio more than you realize. Whether you’re creating a spoken text message to a colleague or giving a speech, you want your audio to shine. Otherwise, you could cause avoidable friction points and potentially hurt your brand reputation. For example, let’s say you create a

chrome os developer mode

How to Turn on Chrome OS Developer Mode

Google’s Chrome OS is a popular operating system that is widely used on Chromebooks and other devices. While it is designed to be simple and user-friendly, there are times when users may want to access additional features and functionality. One way to do this is by turning on Chrome OS