Interfaces VS Templates for Dependency Injection in C++

Interfaces vs Templates for dependency injection in C++

I think interface option is better, but one doesn't have to create common base class just for test. You can inherit your mock class from production class and override necessary methods. You'll have to make the methods virtual though, but that's how tools like mockpp work and they also allow automate this process a little bit.

C++ elegant templates injection into interface

This is doable with the Visitor design pattern.

Visitor injects new functionality into a hierarchy of classes. This need not be virtual and can be expressed as a function template with no problems.

The usual downsides of Visitor apply (the cyclic dependency), as do the usual fixes/workarounds (the acyclic dynamic visitor technique).

Here's a (regular, cyclic) implementation example, quickly thrown together.

#include <iostream>
#include <typeinfo>

class Manager1;
class Manager2;

class Visitor
{
public:
virtual void visit (Manager1*) = 0;
virtual void visit (Manager2*) = 0;
};

class IManager
{
public:
template<class T> void Set(T* t);
template<class T> T *Get();

virtual void accept(Visitor* v) = 0;
};

class Manager1: public IManager
{
public:
template<class T> void Set(T*)
{ std::cout << "Manager1::Set " << typeid(T).name() << std::endl; }
template<class T> T *Get()
{ std::cout << "Manager1::Get " << typeid(T).name() << std::endl; return 0; }
virtual void accept(Visitor* v)
{ v->visit(this); }
};

class Manager2: public IManager
{
public:
template<class T> void Set(T* t)
{ std::cout << "Manager2::Set " << typeid(T).name() << std::endl; }
template<class T> T *Get()
{ std::cout << "Manager2::Get " << typeid(T).name() << std::endl; return 0; }
virtual void accept(Visitor* v)
{ v->visit(this); }
};

template <class T>
class GetVisitor : public Visitor
{
public:
T* GetFunc(IManager* m) { m->accept(this); return t; }
void visit(Manager1* m) { t = m->Get<T>(); }
void visit(Manager2* m) { t = m->Get<T>(); }
private:
T* t;
};

template <class T>
class SetVisitor : public Visitor
{
public:
void SetFunc(IManager* m, T* tt) { t = tt; m->accept(this); }
void visit(Manager1* m) { m->Set(t); }
void visit(Manager2* m) { m->Set(t); }
private:
T* t;
};

template<class T> void IManager::Set(T* t)
{ SetVisitor<T> v; v.SetFunc(this, t); }
template<class T> T *IManager::Get()
{ GetVisitor<T> v; return v.GetFunc(this); }

class Foo {};

int main ()
{
IManager* mgr1 = new Manager1;
IManager* mgr2 = new Manager2;

int a = 5;
const char* b = "abc";
double c = 1.0;
Foo d;

mgr1->Set(&a);
mgr1->Set(&b);
mgr1->Set(&c);
mgr1->Set(&d);
mgr1->Get<Foo>();

mgr2->Set(&a);
mgr2->Set(&b);
mgr2->Set(&c);
mgr2->Set(&d);
mgr2->Get<Foo>();

}

It is possible to break the cycle with some dynamic_casts, but each user of Set and Get will still depend on all Manager classes. This is how templates work in C++. If this is not acceptable, then templates perhaps are not the right tool for this job.

Why prefer template method over dependency injection?

Regarding the "template method" (rather than the design pattern), the following example might help with the pros and cons as to deciding what to do. The example is to create a verbose mode of a library intended to help debugging/developing.

With templates

struct console_print
{
static void print(const string& msg) {std::cout<<msg;}
};

struct dont_print
{
static void print(const string& msg) {}
};

template<printer>
void some_function()
{
printer::print("some_function called\n");
}

The library user can then write:

some_function<console_print>(); //print the verbose message;
some_function<dont_print>(); //don't print any messages.

The benefit of this code is if the user doesn't want the code to be printed, then the calls to dont_print::print(msg) vanish completely from the code (empty static classes get optimised away easily). Such debug messages can then be entered into even performance critical regions.

The disadvantage with templates is that you need to decide your policy before compiling. You also need to change the function/class signature of whatever your templating.

without templates

The above could of course be done with something like:

struct printer 
{
virtual void print(const std::string& msg) = 0;
}

struct console_print : public printer
{
void print(const std::string& msg) {std::cout<<msg;}
}
struct debug_print : public printer
{
void print(const std::string& msg) {}
}

The advantage to this that you can pass around printer types to your classes and functions, and change them at run time (might be very useful for some applications). However, the cost is that a call is always made to the virtual function, and so the empty dont_print does have a small cost. This may or may not be acceptable for performance critical regions.

Dependency injection tradeoff between constructor and template parameters in C++?

It is the same trade off that using templates versus inheritance-based polymorphism always has: template instantiation happens at compile time and thus allows compile time polymorphic behavior.

Inheritance-based polymorphism happens at runtime. You can therefore, for example, store a collection of pointers to objects that you do not know the types of at compile time e.g.

//...
std::vector<Interface*> some_stuff = get_heterogeneous_concrete_types();
std::vector<ConstructorDI> di_objects;
std::transform(some_stuff.begin(), some_stuff.end(), std::back_inserter(di_objects),
[](auto* p) { return ConstructorDI(*p); }
);
//...

You can't do the above if you have multiple DI types that are parametrized on the concrete types. But if you don't need that kind of behavior the templated version will be more efficient, etc.

Dependency injection + full virtual vs interfaces

The advantage of sticking to best practices, design patterns or other idioms is that although you make a bit of extra effort now, you gain more in the long run.

Imagine the scenario where you work in a team, with multiple developers, some experienced, some not.

You are the creator of the Zoo mechanism, but you decide, that for the time being, you will implement the Zoo on a KISS principle without adding the extra abstraction . You set yourself a mental note (or a even a nice little comment) stating that "If there shall be multiple distinct behaviors of the RoboticFeedingSvc there shall be Abstraction over the dependency injection !".

Now , because of your really awesome work, you get to go on a vacation and some junior developer will remain to mantain your code.

One of the tasks of the developer will be to introduce a ManualFeeding option and a hybrid option. How many ways to do this can you think about (with disregards to any coding principle) ?

Because you, the creator, didn't enforce the way the mechanism grows, the junior developer will look at your comment, add a "LoL u mad bro :) " comment , and then choose one of the following :

  • Create a base interface to be derived by other FeedingSvcs (you got lucky here)
  • Create a dependency injection to the RobotFeedingSvc using a strategy pattern (have some functors to be set in terms of how to feed something)
  • Make RobotFeedingSvc a composite between Feeder, Feeded, and some Action function
  • Make the RobotFeedingSvc a singleton factory (because singletons factories are awesome and fancy ) that somehow is used inside the Zoo to return the apropriate feeding technique (important thing here is that he used singleton and factory)
  • Create a templated version of the Zoo that takes a templated version of RobotFeedingSvc that is partially sepecialized according to given FeedingPolicy and Feeder (because he just bumped into templates, and templates should be used everywhere).

I guess we could sum up the story in fewers lines :

Making the initial effort to properly make the abstractions layer required in your application to make it scalable in terms of functionality will help other developers (including here future you ) to quickly understand the proper way to implement new features using the existing code instead of just hacking through it with some wild ideas.

Forcing your Zoo Class to take an interface instead of a concrete class is pretty much equivalent to leave a comment saying that new functionalities need to implement this interface.

Allowing a concrete class to be passed as parameter might switch focus on how to change the concrete class rather then implement something on top of it.

Another more technical reason would be the following :

He needs to add new functionality , but he's not allowed to change the Zoo implementation. What now ?

Dependency injection: templates (/generics) or virtual functions?

I'm probably biased, but I'd say the C++ version is better. Among other things, polymorphism carries some cost. In this case, you're making your users pay that cost, even though they receive no direct benefit from it.

If, for example, you had a list of polymorphic objects, and want to manipulate all of them via the base class, that would justify using polymorphism. In this case, however, the polymorphism is being used for something the user never even sees. You've built in the ability to manipulate polymorphic objects, but never really used it -- for testing you'll only have mock objects, and for real use you'll only have real objects. There will never be a time that you have (for example) an array of database objects, some of which are mock databases and others of which are real databases.

This is also much more than just an efficiency issue (or at least a run-time efficiency issue). The relationships in your code should be meaningful. When somebody sees (public) inheritance, that should tell them something about the design. As you've outlined it in Java, however, the public inheritance relationship involved is basically a lie -- i.e. what he should know from it (that you're dealing with polymorphic descendants) is an outright falsehood. The C++ code, by contrast, correctly conveys the intent to the reader.

To an extent, I'm overstating the case there, of course. People who normally read Java are almost certainly well accustomed to the way inheritance is typically abused, so they don't see this as a lie at all. This is a bit of throwing out the baby with the bathwater though -- instead of seeing the "lie" for what it is, they've learned to completely ignore what inheritance really means (or just never knew, especially if they went to college where Java was the primary vehicle for teaching OOP). As I said, I'm probably somewhat biased, but to to me this makes (most) Java code much more difficult to understand. You basically have to be careful to ignore the basic principles of OOP, and get accustomed to its constant abuse.



Related Topics



Leave a reply



Submit