Non-Virtual Interface Pattern / Non-Virtual Idiom (NVI)
The non-virtual interface pattern (NVI) controls how methods in a base class are overridden. Such methods may be called by clients and overridable methods with core functionality.
It is a pattern that is strongly related to the Template Method Pattern.
Introduced in CS247.
Motivation
public
virtual methods are trying to do two things at once:
- public: Providing an interface to the client
- will uphold invariants
- Respect pre-conditions / post-conditions
- expected to do its job
- virtual: providing an interface to subclasses
- Virtual methods are those that may be customized while an object is used polymorphically
- Provide “hooks” to allow customization of behaviour
Satisfying the responsibilities of “public” and “virtual” simultaneously is difficult
- How are we sure a public virtual method will satisfy its invariants, pre-conditions / post-conditions. Will it even do its job?
- What if we want to add more code to the public virtual method without changing the interface?
Non-Virtual Idiom
Non-Virtual Idiom states the following:
- All public methods are non-virtual
- All virtual methods should be private (or protected)
- Exception for the Destructor, which is public
Example: DigitalMedia
With NVI:
Now: We can add code that must run for all types of DigitalMedia
before or after the doPlay
call.
- we could copyright checking before
doPlay
, this will always run - Or: update a play count after
doPlay
- now subclasses don’t have to worry about maintaining this invariant
Flexible: can provide more “nodes” for customization simply by adding more private virtual method calls.
E.g.: showArt()
before doPlay()
- private virtual display poster for movie, album cover for song, etc.
All can be done without changing the public interface! Which is good for minimal recompilation, open / closed principle.
- In general, easier if we constraint what subclasses do from the beginning, as opposed to wresting back control. Supporting Liskov Substitution Principle
- Any decent compiler will optimize the extra function call out - so no cost at runtime