views:

133

answers:

3

Hi, This is similar to one of my other questions, but different enough I think to warrant a new question.

Basically I am writing a user interface, and my user interface has nodes that can be selected. When a node is selected, the user interface ends up with an abstract node base class "INode". From this I get a factory by doing node->getFactory(), and from this I can create the appropiate dialogs or views for that node because the correct factory gets returned by the concrete node (e.g. factory->createAddDialog(), factory->createView(node), etc.).

My question is about trying to find the best way for that factory to get into the node in the first place.

So far I've thought of 3 ways:

1) Inject the correct factory when I create the node:

AreaNode *node = new AreaNode(new AreaNodeFactory());

So the definition of AreaNode is:

AreaNode : public INode
{
    AreaNode(INodeAbstractFactory *injectedFactory)
    {
        m_injectedFactory = injectedFactory;
    }

    INodeAbstractFactory* getFactory()
    {
        return m_injectedFactory;
    }

    INodeAbstractFactory* m_injectedFactory;
};

2) Inject a more general factory and allow the node to get the factory from that factory:

AreaNode : public INode
{
    AreaNode(IFactory *injectedFactory)
    {
        m_injectedFactory = injectedFactory;
    }

    INodeAbstractFactory* getFactory()
    {
        return m_injectedFactory->getAreaNodeFactory();
    }

    IFactory* m_injectedFactory;
}

3) Just create the concrete factory (though this removes the scope for using different factories for the same node perhaps for testing or for later changes):

AreaNode : public INode
{
    INodeAbstractFactory* getFactory()
    {
        return new AreaNodeFactory();
    }
}

Current thoughts on these options:

Option 1: Could be a little haphazard - I'd have to make sure I always give it the correct factory for that type, or maybe I could just use another factory to inject the correct factory for me.

Option 2: Forces the node to know about the abstract factory implementation enough to be able to call getAreaNodeFactory, which may not be such a bad thing. It at least helps ensure the correct/same factory will always be fetched (assuming the more general factory is implemented properly).

Option 3: This feels little restrictive as I won't be able to swap the class out, and I'm not keen on the node having to know about the concrete implementation of the factory - though in this case it might not be too much of an issue (famous last words!).

Any thoughts on this?

Thanks.

EDIT: Sorry missed out the variable declarations in the origin post, corrected.

EDIT: Another problem with option 2 is I have to implement "getFactory" in every node type. At least with option 1 the base class can just return the inject abstract factory class every time..

+2  A: 

How about.

template<typename Factory> 
class AreaNode : public INode
{
public:
      virtual ~AreaNode(){}

      AreaNode() : pFactory_(new Factory())
      {         
      }

      const shared_ptr<IFactory>& GetFactory()
      {
          return pFactory_;
      } 
private:          
      shared_ptr<IFactory> pFactory_;
};

EDIT:

Or depending on your context.

template<typename Factory> 
class Node
{
public:
      virtual ~Node(){}

      Node() : pFactory_(new Factory())
      {         
      }

      const shared_ptr<IFactory>& GetFactory()
      {
          return pFactory_;
      } 
private:          
      shared_ptr<IFactory> pFactory_;
};

class AreaNode : public Node<AreaNodeFactory>
{
     // Implementation
};

// OR

typedef Node<AreaNodeFactory> AreaNode;
ronag
@ronag: nice idea - though what do you think of the whole idea of nodes even knowing about factories? Kurt above thinks it's a bad idea, however I can't think of a neat way around nodes knowing at least *something* without resorting to the visitor pattern.
Mark
Neither of those examples really work. In the first case you end up with a separate AreaNode type for each Factory - the Factory type should be internal and doesn't need to be exposed by the class's type. In the second case none of the Node instantations share a common base class.
jon hanson
The first example could be improved by only making the constructor templated - it's the only part of the class that needs to know the factory type.
jon hanson
+1  A: 

How about

class FactoryBase { /* interface */ }
template <typename NodeType> class Factory : public FactoryBase {
  // Default implementation.
}
// Add appropriate specializations for all relevant nodetypes, if needed.
template <typename NodeType> inline Factory<NodeType> getFactory(NodeType*) {
  return Factory<NodeType>( );
}

class AreaNode : public Node {
  FactoryBase getFactory() { return getFactory(this); }
};

Template argument deduction will ensure that the factory type is derived from this. This should prevent manual mistakes there. But in general, I wouldn't bother with publicly exposing a factory at all. Just add the following bits to the base class:

class Node {
public:
  std::Auto_ptr<View> createView( ) {
    this->getFactory()->createView(this);
  } // etc.
private:
   virtual FactoryBase* getFactory() = 0;
};
MSalters
@MSalters: I was trying to keep nodes from having such methods - it just seemed a little wrong for the nodes to "know" about such methods as "createView", but maybe I'm wrong.
Mark
Individual node types wouldn't. They just reference factories. API details are restricted to the base class, and there it exists to pass `this` to the factory methods.
MSalters
@MSalters: Okay I see what you mean - on the other hand there is Kurt's comment above that nodes should not have reference to any factories - so again I am torn. Initially I used visitors to do double dispatch, but it just got too cumbersome to use them for every action, and they got very big due to the number of types of node.
Mark
+1  A: 

Depends on the purpose of the "node" objects

If a factory should be injected into a node and concrete node doesn't have to do anything with concrete type of the factory then all factory related code can be moved into the base node class simplifying everything.

class INode //well, it's not "interface" in clear sense
{
public:
     void setFactory(Factory* f) { this->f = f; }
     Factory* getFactory() const { return f; }
private:
     Factory* f;
};

Now, the functionality of concrete nodes and concrete factories is made orthogonal to each other and can be freely combined in any way.

If a concrete type of node is bound to a concrete type of factory, then may be you should avoid factories at all and create views directly with node::createView method. Depends on whether the factories are used in some other context.

Also consider this (not very OOP-ish way):

typedef boost::function0<Factory*> Node;
std::map<UIItem*,Node> nodes;
nodes[area_item1] = &getAreaFactory;
nodes[area_item2] = &getAreaFactory;
nodes[region_item1] = &getRegionFactory;
...
void OnSelect(UIItem* i)
{
    ... 
    View* v = nodes[i]()->createView();
    ...
}

Perhaps, it suits all your needs )

Alsk