In this post, we are going to discuss different implementations of runtime polymorphism.
source: https://www.goodfreephotos.com/Free-Stock-Photos/notebook-and-pen.jpg.php [public domain]

The NVI, or Non-Virtual Interface design pattern, is in a way a mechanism to extend the functionality of a function, just alike inheritance allow classes to be extended. It’s one way of implementing the strategy pattern.

Imagine that you have two processes that look a bit the same:

void process_a() {
    step1();
    step2_variant_a();
    step3();
}
void process_b() {
    step1();
    step2_variant_b();
    step3();
}
void usage(Type process_type) {
    if (process_type == Type::process_a) {
        process_a();
    }
    else if (process_type == Type::process_b) {
        process_b();
    }
}

Duplicating code is often an issue, and for many reasons. When you will eventually find bugs on one of the copy, you may forget to fix the other one. Thereafter, maintaining the code will become painful because both copies will have diverged, and one of them is still bugged. In any cases, duplication increase maintenance cost.


One naive way of removing the duplication could be to create a class hierarchy, and use a virtual function as your customization point.

class GenericProcess {
public:
    virtual ~GenericProcess() = 0;
    virtual void process() = 0; // this is our customization point
protected:
    void step1();
    void step3();
};
class VariantA: public GenericProcess {
public:
    void process() override {
        step1();
        step2_variant_a();
        step3();
    }

private:
    void step2_variant_a();
};

class VariantB: public GenericProcess {
public:
    void process() override {
        step1();
        step2_variant_b();
        step3();
    }

private:
    void step2_variant_b();
};
void usage() {
    auto process = VariantA();
    process.process();
}

The calling side is much cleaner but this is much more verbose, and this doesn’t address the duplication issue.


This is where the non-virtual interface pattern comes into play. Instead of having your public interface process() a virtual function, and all the step() functions non-virtual, we will make the interface non-virtual and move the customization point inside the class.

class GenericProcess {
public:
    virtual ~GenericProcess() = 0;

    // note: the public interface isn't virtual anymore
    void process() {
        // We can clearly see that the algorithm is mostly the same for
        // all variation of that process.
        step1();
        step2();
        step3();
    }

private:
    void step1();
    virtual void step2() = 0; // this is our customization point
    void step3();
};
class VariantA: public GenericProcess {
private:
    // implementation of the customization point for variant A
    void step2() override;
};

class VariantB: public GenericProcess {
private:
    // implementation of the customization point for variant B
    void step2() override;
};
void usage() {
    auto process = VariantA();
    process.process();
}

That time, the code is fully factorized. It is still a bit verbose because of C++ syntax, but we no longer have duplication, and our customization point is clearly identified.

As a side note, GenericProcess can provide a default implementation of step2(). You can also skip the private keyword in the child class since it’s the default visibility in a class. And finally, we could have use operator() instead of process() for our public interface.


Since C++14, it is also possible to implement a similar pattern with generic lambdas:

void step1();
void step3();

auto process_builder = [](auto step2) {
    return [=]() { // note: step2 must be captured by copy
        step1();
        step2();
        step3();
    };
};
void step2_variant_a();
void step2_variant_b();
void usage() {
    auto process = process_builder(step2_variant_a);
    process();
}

Creative Commons License