#ifndef CLASS_DEF
#define CLASS_DEF
#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
namespace gdk {
class MyClass;
typedef shared_ptr< MyClass > MyClassPtr;
class MyClass {
MyClass();
public:
static MyClassPtr MyFactory() {
return MyClassPtr(new gdk::MyClass());
}
void myFunc() {}
};
}
#endif
Boost shared_ptr and factories
As a general coding practice would it be a good idea to not have a public constructor, but rather have a factory that would return a shared_ptr to the class? I know this won’t work for all cases, but as a general rule for higher-level classes? It was just something I started thinking about earlier today so thought I would get some input on it.
Always- or never- rules are rarely correct. [wink] Doing things this way complicates the interface a little (because people will expect to be able to just call constructors, but more importantly because they'll have to work with a smart pointer), and forces the objects to be created on the heap (which can cause performance issues that then won't be work-around-able). Further, it very much complicates the process of copying an object.
That said, sometimes you don't *want* users to have those kinds of freedoms. ;) And if you were, for example, implementing an entirely new *language*, it would be perfectly OK to do this kind of thing under the hood in the language implementation.
Factory methods usually have a name like 'create', and would be consistent across the entire spectrum of things. It is actually possible to do some templating magic to avoid lots of copy-and-paste work of the design pattern:
... But maybe that isn't really any better... x.x Now we create structs to wrap the arguments (unless deeper magic is involved), and inherit from the factory class, to avoid writing the static create function each time. (Maybe more cleanup is possible using the Curiously Recurring Template Pattern, but it seems to me like there are problems with access levels that way...)
That said, sometimes you don't *want* users to have those kinds of freedoms. ;) And if you were, for example, implementing an entirely new *language*, it would be perfectly OK to do this kind of thing under the hood in the language implementation.
Factory methods usually have a name like 'create', and would be consistent across the entire spectrum of things. It is actually possible to do some templating magic to avoid lots of copy-and-paste work of the design pattern:
// We design our classes so their constructors take a single argument - a// struct holding the necessary arguments - by const reference. (Or you could// use some of the Boost metaprogramming stuff ;) )struct nil_t {};template <typename T, typename args>struct factory { typedef boost::shared_ptr<T> handle; static handle create(const args& a) { return handle(new T(a)); } static handle create() { return handle(new T()); }};// functions can deduce template arguments but class constructors can't,// so we add this convenience function, akin to std::make_pair.template <typename T, typename args>factory<T, args>::handle create(const args& a) { return factory<T, args>::create(a);}// Not a partial specialization; the argument list differs :)template <typename T>factory<T, nil_t>::handle create() { return factory<T, nil_t>::create();}// Now you canjust do like this for each class:namespace gtk { struct MyClassArgs { int foo; int bar; MyClassArgs(int foo, int bar): foo(foo), bar(bar) {} }; class MyClass { int foo; int bar; friend class factory<MyClass, MyClassArgs>; MyClass(const MyClassArgs& a) : foo(a.foo), bar(a.bar) {} }; class YourClass { friend class factory<YourClass, nil_t>; YourClass() {} // private, lol };}create<gtk::MyClass>(gtk::MyClassArgs(1, 2));create<gtk::YourClass>();
... But maybe that isn't really any better... x.x Now we create structs to wrap the arguments (unless deeper magic is involved), and inherit from the factory class, to avoid writing the static create function each time. (Maybe more cleanup is possible using the Curiously Recurring Template Pattern, but it seems to me like there are problems with access levels that way...)
Not providing a default constructor is a good idea whenever no such constructor can exist, because the class cannot be initialized without some necessary parameters (it must, for instance, reference existing objects).
Hiding the default constructor and providing a static factory method in the same class is a violation of the single responsibility rule, because the object becomes responsible both for its original task, and for whatever task the factory method does that the constructor doesn't do.
Hiding the default constructor and providing an external factory object with friend access creates strong coupling between the object and its associated factory. Unless it is absolutely necessary to have this coupling (for instance, to have a DLL allocate the memory, instead of the program), don't do it. I've found that things are usually smoother if classes which are created from factories also have public constructors, because you have more liberty in using them. What if I don't want to have a shared pointer?
Hiding the default constructor and providing a static factory method in the same class is a violation of the single responsibility rule, because the object becomes responsible both for its original task, and for whatever task the factory method does that the constructor doesn't do.
Hiding the default constructor and providing an external factory object with friend access creates strong coupling between the object and its associated factory. Unless it is absolutely necessary to have this coupling (for instance, to have a DLL allocate the memory, instead of the program), don't do it. I've found that things are usually smoother if classes which are created from factories also have public constructors, because you have more liberty in using them. What if I don't want to have a shared pointer?
Normally, the purpose of factory methods, factory classes, and other creational patterns is encapsulating knowledge about how objects should be initialized; locking down constructors can be a complementary safety measure against unsafe object creation, but returning shared pointers is irrelevant.
Why should your factory return shared pointers, and what do you need factories for?
To decide if your idea is a good idea in your specific context, you need to answer ToohrVyk and Zahlman's arguments against its three aspects (providing factories, nonpublic constructors and returning shared_ptr<>) for every class and subsystem in your program.
I could add more arguments of my own, for example that "higher-level classes" are a vague and slippery concept that has nothing to do with creational patterns.
Why should your factory return shared pointers, and what do you need factories for?
To decide if your idea is a good idea in your specific context, you need to answer ToohrVyk and Zahlman's arguments against its three aspects (providing factories, nonpublic constructors and returning shared_ptr<>) for every class and subsystem in your program.
I could add more arguments of my own, for example that "higher-level classes" are a vague and slippery concept that has nothing to do with creational patterns.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement