8
\$\begingroup\$

I found a definition of the factory design pattern as:

Define an interface for creating an object, but let the subclasses decide which class to instantiate.

I understood the first part "defining interface for creation an object", but I am not able to understand the second part of "let subclasses decide which class to instantiate".

Is the following example a correct implementation of factory design pattern? If yes, please help to me to understand second part of definition with respect to the example.

        #include <iostream>
    using namespace std;

    enum genre_e{ROCK,POP, REGGAE, INVALID};

    /*Base Class*/
    class Music {
    public:
     virtual void song() = 0;
    };

    /*Derived class Rock from Music*/
    class Rock: public Music
    {
    public:
     void song()
     {
      cout<<"Nirvana: Smells like a teen spirit\n";
     }
    };

    /*Derived class Pop from Music*/
    class Pop: public Music
    {
    public:
     void song()
     {
      cout<<"Michael Jackson: Billie Jean\n";
     }
    };

    /*Derived class Reggae from Music*/
    class Reggae: public Music
    {
    public:
     void song()
     {
      cout<<"Bob Marley: No woman, No cry\n";
     }
    };

    /*Factory Class*/
    class MusicFactory
    {
    public:
     /*Factory Method*/
     Music *getMusic(genre_e genre)
     {
      Music *music = NULL;

      /*Logic based on Genre*/
      switch(genre)
      {
      case ROCK:
       music = new Rock();
       break;
      case POP:
       music = new Pop();
       break;
      case REGGAE:
       music = new Reggae();
       break;
      default:
       music = NULL;
       break;
      }
      return music;
     }
    };

    int main()
    {
     /*Create factory*/
     MusicFactory *musicFactory = new MusicFactory();

     /*Factory instantiating an object of type ROCK*/
     Music *music = musicFactory->getMusic(ROCK);

     cout<<"Song: ";
     if(music)
      music->song();
     else
      cout<<"Wrong selection dude/dudette !!";
    }
\$\endgroup\$
1
  • \$\begingroup\$ Was this code that you wrote or did you find it somewhere? \$\endgroup\$ Commented Feb 4, 2021 at 21:46

3 Answers 3

13
\$\begingroup\$

You have implemented is a factory pattern.

What you describe in the first paragraph is an Abstract factory pattern. Slightly different.

An abstract factory allows you to plug in one of multiple different factory's in at run-time to get different situations.

As a big note. Stop using pointers like that. That is a complete no no in C++

  1. Do you need to create the factory dynamically.
    A local object will probably do.

  2. Even though the factory probably needs to create the objects dynamically you don't want to return pointers (but rather a smart pointer or a reference depending on how you want to handle ownership semantics (there are several good articles on SO about this so go have a read)).

Example:

class MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre) = 0;
};

class AsianMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};
class AfricanMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};
class EuropeanMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};
class SouthAmericannMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};
class NorthAmericannMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};
// Special factory used when running unit tests.
class UnitTestMusicFactory: public MusicFactory
{
    public:
        virtual std::unique_ptr<Music> getMusic(genre_e genre);
};

So you have a bunch of different factories that all implement the same interface. But how do you know which one you should use. A common way is for a single creation point. Just ask the system it will create one for the lifetime of the application.

MusicFactory& getMusicFactory()
{
     // Note the static here.
     // It will only be created and initialized once (on first call).
     static std::unique_ptr<MusicFactory>  factory = createMusicFactory();
     return *factory;
}
// Never call this method directly.
// This function should only be called by getMusicFactory().
std::unique_ptr<MusicFactory> createMusicFactory()
{
     // Get info from user configuration.
     // Command line arguments. IP information etc whatever helps you
     // Make the decision on the actual factory that should be instantiated.
     // So we can decide what factory should be used by the application.
     std::unique_ptr<MusicFactory>  result(new XXXXX);
     return result;
}

int main()
{
    // Get the factory.
    MusicFactory&          fac   = getMusicFactory();

    // Use the factory to create the music.
    // At compile time we don't know what continents the music is from
    // as this is decided at run-time by he system and what factory is
    // created.
    std::unique_ptr<Music> music = fac.getMusic(ROCK);

    cout<<"Song: ";
    music->song();   // Should test for NULL
}
\$\endgroup\$
2
2
\$\begingroup\$

"Define an interface for creating an object, but let the subclasses decide which class to instantiate" is from the seminal GoF book Design Patterns: Elements of Reusable Object-Oriented Software, chapter **"3.3 Factory Method".

Design Patterns

It refers to a factory method as opposed to an abstract factory (as noted by previous posters), and the motivation is that the client code -- which uses the object(s) being manufactured -- is itself encapsulated in a class (rather than a few lines of code directly in main() as in simple examples here). See this.

The idea is that the client (e.g. application) class itself is reusable and has concrete subclasses which implement the create(...) method to create the specific object(s) to use. Note that in such context a create() may return only a single type specific to the client, or it may be parameterized -- the Parameterized factory methods variation -- by a type indicator argument, as in your example.

So what user3665615 has implemented is just one concrete Factory class w/o the abstract base Factory (Loki Astari provides the full abstract factory example), while the quote at the beginning of the question refers to a Factory Method pattern, thus the confusion.

And yes, to second Loki Astari, please don't use bare pointers in modern C++ -- C++ is different from C and Java.

Ownership of dynamically allocated objects is a crucial concept in the C++ design, since the owner is responsible for destroying them. In (rare) cases when object ownership is shared by the factory/creator and the client/other code, create(...) may return a std::shared_ptr instead of std::unique_ptr.

\$\endgroup\$
0
\$\begingroup\$

Yes, this is a good example of a concrete factory design pattern. In essence, in C++, what we strive to do when we create an Object factory class is to be able to pass some piece of dynamic information (a type identifier) and create a class of appropriate static type from this.

So in general, in your factory, you will have some Create function which accepts a parameter which can be used as a type identifier and returns a pointer to a base class. For instance, we might have a CShapeFactory which has the following Create method:

CShape* CShapeFactory::Create( std::string szShapeName )
{
     if( szShapeName == "Line" )
     {
         return new CLine;
     }
     else if( szShapeName == "Square" )
     {
         return new CSquare;
     }
     else
     {
         throw std::invalid_argument( "Unknown argument passed to Shape Factory Create() method" );
     }
}

You see here we are dynamically creating a different derived class based on run-time information, this is useful in circumstances when the user is often performing actions which require the creation of different related classes (i.e. a Document Manager for instance).

The other main type of factory design pattern is the Abstract design pattern, which is what you describe. This is where your factory class is an abstract implementation and you derive other factory classes from it, allowing for a further level of dynamic choice in your application. For instance, we could create the following abstract interface:

class IShapeFactory {
public:
      virtual CShape* Create( std::string szShapeName ) = 0;
};

And then implement the following two derived factory classes:

class CNormalShapeFactory : public IShapeFactory {
public:
      CShape* Create( std::string szShapeName ) {
           if( szShapeName == "Line" )
           {
               return new CLine;
           }
           else if( szShapeName == "Square" )
           {
               return new CSquare;
           }
           else
           {
               throw std::invalid_argument( "Unknown argument passed to Shape Factory Create() method" );
           }
      }
};

class CMagicShapeFactory : public IShapeFactory {
public:
      CShape* Create( std::string szShapeName ) {
           if( szShapeName == "Line" )
           {
               return new CMagicLine;
           }
           else if( szShapeName == "Square" )
           {
               return new CMagicSquare;
           }
           else
           {
               throw std::invalid_argument( "Unknown argument passed to Shape Factory Create() method" );
           }
      }
};

An application of this would be if you had a user option which determined if they wanted to create magic shapes or normal shapes, and then you choose which factory to use depending on the option whilst improving code maintainability and readability by using the same IShapeFactory* pointer.

\$\endgroup\$
8
  • 3
    \$\begingroup\$ Please stop creating interfaces that pass around RAW pointers. Interfaces should define the ownership semantics of any objects that are dynamically created. \$\endgroup\$ Commented Jul 13, 2014 at 16:25
  • \$\begingroup\$ @LokiAstari Surely it is an implementation detail of the factory as to whom the ownership of the object passes to once it has been created? \$\endgroup\$ Commented Jul 13, 2014 at 16:27
  • \$\begingroup\$ @LokiAstari Ahh, on reflection I think I understand what you mean; by returning a std::unique_ptr we "guarantee" (up to reasonable doubt) that this is the only copy of the pointer floating around and that when we delete our abstract factory, our pointer will still be valid. Is this what you mean? \$\endgroup\$ Commented Jul 13, 2014 at 16:33
  • 2
    \$\begingroup\$ If you have different factories implementing the ownership semantics in different ways then your program is going to break (because an application will work with one factory CNormalShapeFactory but then because of assumptions made about that factory the same code will break when used with CMagicShapeFactory). So no all factories must implement the same semantics. Also pointers do not convey any ownership semantics. So your code is now no longer any better than C code which means it can be used incorrectly. The point of C++ is that we can define our interfaces so they can not be misused \$\endgroup\$ Commented Jul 13, 2014 at 16:34
  • \$\begingroup\$ There are multiple ways of doing this (the one I use above std::unique_ptr basically passes ownership back to the caller). Alternatively you can use std::shared_ptr to indicate shared ownership or you can return by reference indicating that the ownership is retained by the factory. All techniques have their pluses and minus. \$\endgroup\$ Commented Jul 13, 2014 at 16:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.