Thread: Abstract Factory pattern

  1. #1
    Registered User
    Join Date
    Mar 2003
    Posts
    28

    Abstract Factory pattern

    (I hope that it's okay to ask a question on design patterns here. It's not C++ specific but, well, I plan to implement the pattern in C++, and no other board seemed more appropriate.)

    At the moment, the code I'm working on has a whole bunch of "Factories". By a "Factory", I simply mean a class that contains a whole bunch of methods that return instances of objects. These classes have been implemented as Singletons. For example:

    Code:
    class WidgetFactory
    {
       static WidgetFactory& getInstance();
       Widget getWidget();
    };
    So, to get a Widget, client code calls WidgetFactory::getInstance().getWidget();

    Now, I'm trying to understand how the Abstract Factory pattern can help here so that the creation of Widgets aren't decided at compile time. For example, there might be two types of Widgets: SmallWidgets and BigWidgets (yeah, my examples suck, but it's all that's coming to me at the moment ).

    If I understand the Abstract Factory pattern correctly, then this is implemented like this:

    Code:
    class WidgetFactory
    {
       virtual Widget* getWidget() = 0;
    };
    
    class SmallWidgetFactory
    {
       virtual Widget* getWidget() { return new SmallWidget(); }
    };
    
    class BigWidgetFactory
    {
       virtual Widget* getWidget() { return new BigWidget(); }
    };
    Okay, finally to the actual question. What I don't get is how I actually am supposed to instanciate the factory itself. Before, it was simply a singleton, so that wasn't a problem. Now, I need to select whether I want big or small widgets (say, from a configuration file), and then use the appropriate factory. But say I have 10 different classes that all need to create widgets... do they all need to check the config file themselves? Or maybe there should be another class, implemented as a singleton, that is responsible for checking the config file, and returning the factory? (A factory factory?) Rough pseudocode:

    Code:
    class ConfigFileReader
    {
       static ConfigFileReader& getInstance();
       
       WidgetFactory* getWidgetFactory()
       {
           if (config file says small widgets)
           {
               return SmallWidgetFactory;
           }
           else
           {
               return BigWidgetFactory;
           }
       }
    };
    On preview, this question barely seems to make sense, even to me. I guess I'm just drowning in details at the moment. I guess my question is this: does this seem like a reasonable design? Is there a better way to do this? Can I avoid Singletons altogether maybe (I keep getting told I should )?
    "C++ is like jamming a helicopter inside a Miata and expecting some sort of improvement."
    - Drew Olbrich

  2. #2
    Registered User
    Join Date
    Jan 2003
    Posts
    311
    The main trick that you seem to want is the virtual constructor idiom. Here is a simple example
    Code:
    #include <boost/shared_ptr.hpp>
    
    class cloneable {
    public:
        cloneable() {std::cout << "ctor(default)" << std::endl;}
        cloneable(const cloneable &o) {std::cout << "ctor(copy)" << std::endl;}
        typedef boost::shared_ptr<cloneable> handle;
        virtual handle clone() const = 0;
        virtual std::istream & parse(std::istream &is) = 0;
        virtual ~cloneable() {}
    };
    
    // dynamic dispatch, one operator for all
    std::istream & operator >> (std::istream &is, cloneable &clone) {
        return clone.parse(is);
    }
    
    class bigWidget : public cloneable {
        int x,y;
    public:
        bigWidget() : x(0), y(0) {}
        virtual handle clone() const {return handle(new bigWidget(*this));}
        virtual std::istream & parse(std::istream &is) {
            char ch;
            if(is >> ch && ch != '(') is.setstate(std::ios_base::failbit);
            int tx;
            if(is >> tx >> ch && ch != ',') is.setstate(std::ios_base::failbit);
            int ty;
            if(is >> ty >> ch && ch == ')') {
                    x = tx; y = ty; // no syntax errors
            } else is.setstate(std::ios_base::failbit);
            return is;
        }
        virtual
        ~bigWidget() {std::cout << "Dead Big Widget ("<<x << ", " << y << ')' << std::endl;}
    };
    
    class smallWidget : public cloneable {
       int x,y;
    public:
        smallWidget(int xx, int yy) : x(xx), y(yy) {}
        virtual handle clone() const {return handle(new smallWidget(*this));}
        virtual std::istream & parse(std::istream &is) {
            char ch;
            if(is >> x >> ch >> y && ch != ',') is.setstate(std::ios_base::failbit);
            return is;
        }
        virtual
        ~smallWidget() {std::cout << "Dead smallWidget " << x << ", " << y << std::endl;}
    };
    
    class factory {
        typedef cloneable::handle handle;
        typedef std::map<std::string, handle> map_t;
        map_t map;
    public:
        factory() {
            handle h(new bigWidget);
            map["bigWidget"] = h; // only one bigWidget
            map["smallWidget"] = smallWidget(3,4).clone(); // temporary and heap
        }
        handle gen(const std::string & type) const {
            map_t::const_iterator it = map.find(type);
            return (it != map.end())?it->second->clone():handle();
        }
        handle parse(std::istream &is) const {
            std::string type;
            if(is >> type) {
                handle clone = gen(type);
                if(clone && is >> *clone) return clone;
            }
            return handle();
        }
    };
    
    void test() {
        factory f;
        cloneable::handle h;
        while((h = f.parse(std::cin)) && std::cin) std::cout << "OK!\n";
        std::cout << "Out of loop" << std::endl;
    }
    Ok, perhaps not so simple, its kinda fun though.

    This sets up a syntax for your configuration file where you prefix each type with the key for the map in the factory. each decenant of cloneable implements a virtual parse method, that ends up being a "virtual" operator >>. The factory parse method can then build decendants of cloneable without knowing anything about them. The boost::shared_ptr's solve a lot of hassles, but handles could be simple pointers, if you remmeber to call delete.

  3. #3
    Registered User
    Join Date
    Mar 2003
    Posts
    28
    Wow, thanks grib, that's quite impressive. So the config file would contain a list of types of widgets, the factory looks up that type of widget in its map and if it finds it, it returns a clone (copy), otherwise it returns just a generic widget of the base class (a 'cloneable')?
    "C++ is like jamming a helicopter inside a Miata and expecting some sort of improvement."
    - Drew Olbrich

  4. #4
    Registered User
    Join Date
    Jan 2003
    Posts
    311
    Quote Originally Posted by Just
    Wow, thanks grib, that's quite impressive. So the config file would contain a list of types of widgets, the factory looks up that type of widget in its map and if it finds it, it returns a clone (copy), otherwise it returns just a generic widget of the base class (a 'cloneable')?
    Actually it returns null, cloneable is pure virtual in this case. shared_ptr() or in this case handle() is a shared pointer to null. A const static null in cloneable would probably not be a bad idea, this would make it easyer to change to simple pointers. cloneable could be given default clone and parse beavior that simply returns null and sets failbit respectively. Then changeing gen to return a cloneable if the type is not found would be ok. the default clone could return a new clone and you could then rely on parse failing but it's simpler to return null.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Question about the Factory pattern.
    By h3ro in forum C++ Programming
    Replies: 14
    Last Post: 11-27-2008, 04:55 PM
  2. Need comment on Factory Pattern Example
    By Bargi in forum C++ Programming
    Replies: 2
    Last Post: 07-04-2008, 05:46 AM
  3. abstract class
    By xddxogm3 in forum C++ Programming
    Replies: 5
    Last Post: 01-01-2005, 09:08 AM
  4. Abstract Factory in C++
    By agrsaurabh in forum C++ Programming
    Replies: 1
    Last Post: 10-06-2003, 02:16 PM
  5. text pattern recognition
    By mtsmox in forum C++ Programming
    Replies: 5
    Last Post: 02-27-2002, 08:38 AM