Quote:Original post by Burnhard
The way I'm using them comes from the following principles:
(1) Never use "delete"
(2) Only use "addref" or "release" in a custom allocator/deallocator
(3) Only ever use "new" in a class factory
(4) Every class has an abstract interface
(5) Every class has an implementation derived from the abstract interface
(6) The class factory returns instances of the abstract interface
This is the typical IOC/DI approach that has been universally proven to be a solid way to scale the design.
The only thing where it deviates is that it doesn't avoid constructors - it merely requires that no work beyond immediate assignment is performed in them, something which cannot fail. I'm not so sure about using init() for reinitialization, since I've found it over-complicates the invariant and pre-/post-condition verification - it's easier to just drop the instance and ask for a new one. YMMV.
This was long ago ">presented for Java, but it applies to C++ and to a degree even to C (with different syntax). WinAPI is an example of similar approach, where caller is responsible for providing properly constructed and allocated members.
The only thing I dislike about the above is IF/Impl separation. C++ has the advantage of free functions, so much of the logic can be expressed using free functions only, operating on elementary containers and PODs. It doesn't sacrifice anything (just as testable and extensible), but requires considerably less syntactic bloat. May require a slight shift in design approach to grok it.
Another interesting property of this approach, especially in C/C++ is that it gracefully handles the global/singleton issues. Since no instance may be requested directly, factory is the only place responsible for actual allocation, the code remains independent of usual gotchas since logic is never hard-coded to any specific instance so the design doesn't become locked in.