开发者

creating objects from template class with different type

I was not really sure how to formulate my question, but here is the puzzle I am trying to resolve:

if (config.a)
  myObject = new Object<DummyInterface>();
else
  myObject = new Object<RealInterface>();

so the task is to create a object with a dummy interface if it is specified in config, otherwise use real interface class. How do I declare myObject then? there are couple options, I could have Object class to开发者_开发百科 derive from abstract class without templates: i.e.:

class Base
{
   ...
}

template <class T>
class Object : public Base 
{
...
}

Then I could declare myObject as:

Base* myObject;

But here is the problem: what if my Object class declares a non virtual method:

template <class T>
class Object : public Base 
{
 public:
   T getInterface() { return myInterface;}
 private:
   T myInterface;
}

I cannot call it like this:

myObject->getInterface()

and I cannot do dynamic cast, because I don't know the type until the runtime...

Any suggestions how to get around it? Maybe there is a another solution?


One way around is to use the visitor pattern. This way, your base class may implement a visit() method and your derived instances can override...

For example..

SomeComponent
{
  template <typename T>  // I'm being lazy here, but you should handle specific types
  void handle(T& cInst)
  {
    // do something
  }
};

class Base
{
public:
  virtual void visit(SomeComponent& cComp) = 0;
};

template <class T>
class Object : public Base 
{
public:
  virtual void visit(SomeComponent& cComp)
  { 
    cComp.handle(*this);
  }
};

Now you can do this

SomeComponent c;
Base* obj = new Object<int>;
obj->visit(c);

And c will get the correct type.


if (config.a)
  myObject = new Object<DummyInterface>();
else
  myObject = new Object<RealInterface>();

This construction is incorrect in terms of the polymorphism. Two template instantiations are two different classes. The best situation is when you have something like that:

template <class T> SomeClass: public SomeBaseClass 
{
};
......... 
SomeBaseClass* myObject;

But it brings you no profit. The simplest and right solution is the virtual functions. The visitor pattern seems useful too.


I actually think that the visitor pattern would be misused here. Instead, this is a classic switch-on-types code smell that is best handled by polymorphism.

When you say "what if one derived class has an additional method to call", that is assuming a specific design. That is not a functional requirement. A functional requirement would be "what if one of the two objects created had to do behavior X during event Y". Why is this different? Because there are a number of ways to implement this that don't require more interface (though maybe more methods).

Let me show an example.

You have your factory

std::map<ConfigValue, Generator> objectFactory_;

That you've registered a bunch of generators for (probably in constructor of class)

RegisterGenerator(configValueA, DummyGenerator);
RegisterGenerator(configValueB, RealGenerator);
...

And at some point you want to create one of those objects.

shared_ptr<Base> GetConfigObject(ConfigFile config)
{
  return objectFactory_[config.a]();
}

And then you want to use the object for handling an event, you can do

void ManagingClass::HandleEventA()
{
  theBaseObjectReturned->HandleEventAThroughInterfaceObject(this);
}

Note how I passed a this pointer. This means if you have one object that doesn't want to do anything (like make that extra behavior call) that your managing class may provide, it doesn't need to use this.

Object<DummyInterface>::HandleEventAThroughInterfaceObject(ManagingClass *)
{
  // just do dummy behavior
}

And then if you want to do something extra (call a new behavior) it can do it through that pointer in the RealInterface

Object<RealInterface>::HandleEventAThroughInterfaceObject(ManagingClass * that)
{
  that->DoExtraBehavior();
  // then dummy - or whatever order
  // you could even call multiple methods as needed
}

That's the basic approach you should always take when dealing with polymorphism. You should never have two different code paths for different types except through calls to virtual dispatch. You should never have two different code blocks, one that calls methods A, B, and C and another that only calls A and D when dealing with a base object, depending on type. Instead, always make the derived objects do the work of figuring out what to do - because they know who they are. If you need to do stuff in the managing object, pass a this pointer for them to work with.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜