views:

76

answers:

2

What is the difference between NVI ( Non-Virtual Interface ) and the Template Method patterns?

They seem very similar and I've read both that they're basically the same and that they're subtly different with Template being somehow more general.

+4  A: 

NVI is an idiom, Template Method is a pattern. NVI is an implementation of the Template Method Pattern using dynamic dispatch in C++; it is also possible to create template methods in C++ using template metaprogramming to eliminate the dynamic dispatch.

A pattern is more general than an idiom, and languages may use different idioms to implement the pattern.

Pete Kirkham
So you're saying that NVI is basically a language specific implementation of the Template Method pattern and beyond that there's no real difference? How would you use C++ templates to achieve the same result?
Robert S. Barnes
@Robert S. Barnes As far as I can see, there isn't an obvious way of using C++ templates as template method. Template method says 'do this, then do this other thing', and whilst you can create a functor for one or the other of the things to do, there's no real relationship with the type parameters that C++ templates give you.
Pete Kirkham
I guess I'm still not understanding what you meant here: "it is also possible to create template methods in C++ using template metaprogramming to eliminate the dynamic dispatch."
Robert S. Barnes
Normally in a template method, there's some non-virtual calls and some virtual calls. You can put the template method inside a template, or call against template specialisations, so that the calls are resolved at compile time, but you can't directly create a template specialisation which is a template method - you can call template specialisations from the template method but the template specialisation is not the template method itself. e.g. `template <typename T> T fma ( T a, T b, T c ) { return a + b * c; }` will call the correct specialisations or overloads of T::operator+.
Pete Kirkham
+3  A: 

As has been said, NVI is a progamming idiom, related to a category of languages. It's been promoted by Herb Sutter among others, because it helps enforcing contracts:

  • class invariants
  • function contracts (assertions over the parameters passed and the return value generated)
  • repetitive operations (like logging)
  • control over the exceptions generated ( bad idea though ;) )

However, the implementation may actually differ considerably, for example another example of NVI implementation is to combine it with Pimpl:

class FooImpl;

class Foo
{
public:
  enum type { Type1, Type2 };

  Foo(type t, int i, int j);

  int GetResult() const;

private:
  FooImpl* mImpl;
};

And for the implementation:

struct FooImpl
{
  virtual ~FooImpl();
  virtual int GetResult() const;
};

class FooType1: public FooImpl
{
public:
  FooType1(int i, int j);
  virtual int GetResult() const;
private:
  /// ...
};

I've always found that it conveyed the point better. Have you figured it out ?

The main point is that virtual is an implementation detail. And exposing implementation details in the interface is a bad idea, because you may wish to change them.

Furthermore implementation details tend to mess with binary compatibility. For example adding a new virtual method in a class may change the layout of the virtual table (common implementation technic) and thus botch the binary compatibility. On gcc you need to make sure that you add it last (among the virtual) if you wish to preserve the compatibility.

By using the NVI + Pimpl combination above, there is no virtual at all (not even private) in the class exposed. The memory layout is backward and forward compatible. We have achieve binary compatibility.

Here, we use several patterns at once:

  • Template Method
  • Strategy (since we can swap the pointer at will)
  • Factory (to decide which implementation we get)
Matthieu M.
+1 for the "virtual is implementation detail" and the ABI consideration.
neuro
+1. I have to disagree with you about making separate pimpl-like classes for the virtual interface, however. Most obvious downside is that it doubles the number of classes required. Also if FooImpl wasn't opaque, it might tempt people to use it directly and skip Foo. However, it's unlikely that it would be opaque since the point of NVIs are to allow people to override the virtual implementations, so FooImpl would have to be publicly accessible. Even though it has its own downsides, I think implementing NVI in one class and avoiding public virtual functions all together is a better approach.
It's also an easier policy to enforce: no public virtual functions as opposed to one that makes special cases for pimpl-like classes which provide the public virtual interface.
The problem of having `virtual` function is not with ease of use, it's with ABI. The advantage of `FooImpl` being opaque is that you confine it to your binary and thus have no issue of ABI when you deliver new version of the binary to your clients.
Matthieu M.
Great, but how does NVI differ from Template Method? It seems like you're saying that NVI is a specific way of implementing the Template Method in a certain class of languages?
Robert S. Barnes
Template Method is a fancy word. In C++ every time you use the `virtual` keyword you provide a hook of customization, which is essentially what the Template Method boils down. So the Template Method is a pattern while NVI is a guideline about the use of the `virtual` keyword in C++.
Matthieu M.