Factory abstract for pattern class dilemmas

Overview

The technical problem is that the current project requires an abstract factory for the working class of the C ++ template, which, as far as I can tell, is impossible. Thus, I need an alternative solution to prevent clients from hanging on the details of the employee and worker from any particular client environment.

Customization

I have a Worker class that internally requires a container class C to remember process information. Processing information is organized into a BufferType structure, so Worker interacts internally with a member of type C< BufferType > . On the other hand, we do not want Worker clients to know about Worker::BufferType , which reflect irrelevant information about the use of the Worker API, which may change over time.

The situation is complicated by the fact that C is an abstract class that has various implementations of container functionality depending on the actual environment (for example, a database). Obviously, we don’t want Worker know about this, it should work in any environment for which there is a C implementation.

Since I don't have much experience writing factories, I went ahead and wrote the following:

These are the containers I have to deal with:

 template <class T> class C { // container interface }; template <class T> class CImpl : public C<T> { // implements C interface }; 

Here's how I would like to implement the Worker class:

 class Worker { public: Worker( AbstractCFactory& f ) : _f( &f ), _buffer( NULL ) {} void doSomething() { _buffer = _f->create<BufferType>( ); // ... do something with _buffer } private: AbstractCFactory* _f; typedef struct { int someInfo; } BufferType; C< BufferType >* _buffer; }; 

However, this will require something like the following factories:

 class AbstractCFactory { public: template <class T> virtual C< T >* create() = 0; }; class ConcreteCFactory : public AbstractCFactory { public: template <class T> virtual C< T >* create() { return new CImpl< T >(); } }; 

While I thought this was a great design, the compiler was not so enthusiastic and reminded me that template methods cannot be virtual. Now, when I think about it, it makes sense - the compiler cannot know what code should be generated for the template method in the factory if the factory type is determined only at runtime.

Ugly workaround

First, I implemented factories as templates to allow the create() virtual function:

 template <class T> class AbstractCFactory { public: virtual C< T >* create() = 0; }; template <class T> class ConcreteCFactory : public AbstractCFactory { public: virtual C< T >* create() { return new CImpl< T >(); } }; 

Then I moved BufferType to the Worker public interface, allowing the client to instantiate ConcreteCFactory< Worker::BufferType > to pass to the Worker constructor.

 class Worker { public: typedef struct { int someInfo; } BufferType; Worker( AbstractCFactory< BufferType >& f ) : _f( &f ), _buffer( NULL ) {} void doSomething() { _buffer = _f->create( ); // ... do something with _buffer } private: AbstractCFactory< BufferType >* _f; C< BufferType >* _buffer; }; 

Obviously, my solution is not a real solution, because it still introduces an undesirable client dependency on the private details of the Worker ( BufferType ) implementation, exposing them to the public interface.

Question

Is there a right solution that doesn't make me break encapsulation i.e. does not support Worker independent of CImpl , and the client is not dependent on Worker::BufferType ?

+4
source share
2 answers

CImpl<T> is a template class that must be provided by the client, and it must be specialized in CImpl<BufferType> . At time of specialization, both CImpl and BufferType must be defined. One possibility would be to ask the client to provide a CImpl.h file containing a definition of the C<T> implementation:

cimpl.h (provided by the client)

 template <class T> class CImpl : public C<T> { // implements C interface }; 

worker.h:

 class IC { }; template <class T> class C: public IC { // container interface }; class AbstractCFactory { public: virtual IC* create() = 0; }; class Worker { public: Worker( AbstractCFactory& f ) : _f( &f ), _buffer( 0 ) {} void doSomething(); private: AbstractCFactory* _f; C< struct BufferType >* _buffer; }; AbstractCFactory& getDefaultFactory(); 

worker.cpp

 #include "worker.h" #include "cimpl.h" #include <stdlib.h> template <class T> class ConcreteCFactory : public AbstractCFactory { public: virtual IC* create() { return new CImpl< T >(); } }; struct BufferType { int someInfo; }; void Worker::doSomething() { _buffer = static_cast<C<BufferType>*>(_f->create( )); // ... do something with _buffer } AbstractCFactory& getDefaultFactory() { static ConcreteCFactory<BufferType> f; return f; } 

Usage example:

 #include "worker.h" ... AbstractCFactory& f = getDefaultFactory(); Worker w(f); w.doSomething(); 

Another possibility is to split the .h file into 2 parts, one open interface and one private, which the client should never use directly.

worker.h

 class IC { }; template <class T> class C: public IC { // container interface }; class AbstractCFactory { public: virtual IC* create() = 0; }; struct BufferType; class Worker { public: Worker( AbstractCFactory& f ) : _f( &f ), _buffer( 0 ) {} void doSomething(); private: AbstractCFactory* _f; C< struct BufferType >* _buffer; }; AbstractCFactory& getFactory(); #include "private.h" 

private.h:

 struct BufferType { int someInfo; }; template<class I> class CFactory: public AbstractCFactory { public: virtual IC* create() { return new I(); } }; template<class I> AbstractCFactory& getFactory() { static CFactory<I> fact; return fact; } 

worker.cpp:

 #include "worker.h" void Worker::doSomething() { _buffer = static_cast<C<BufferType>*>(_f->create( )); // ... do something with _buffer } 

Usage example:

 #include "worker.h" ... template <class T> class CImpl : public C<T> { // implements C interface }; ... AbstractCFactory& f = getFactory<CImpl<BufferType>>(); Worker w(f); w.doSomething(); 
+1
source

Sometimes using templates is not very good. If you do not want the client to know BufferType about you, you should use the specialized specialization in CImpl with BufferType and create your own worker without a factory template.

0
source

All Articles