Policy-based design
From Free net encyclopedia
←Older revision | Newer revision→
Policy-based design is a programming technique summarized as a compile-time equivalent of the strategy pattern. Andrei Alexandrescu popularized this technique with his book Modern C++ Design (Addison-Wesley, 2001, ISBN 0201704315) and his column Generic<Programming> in the C/C++ Users Journal.
Contents |
Overview
The technique is used to create a flexible set of types, providing the same interface, but employing different implementation behind. Therefore this technique has a lot of similarity to the strategy pattern. However while strategy allows the type to "change its ways" runtime, policy-based design fixes the implementation during compilation. In fact, it creates a new type for each different implementation. While their interface, functions present, their names etc., will be the same, they will be different type - as opposed to strategy, where the same type behaves differently.
The main idea is to use commonality-variability analysis to divide the type into the fixed implementation and interface, the policy-based class, and the different policies. The main class, the policy-based class takes template arguments, types, templates of types etc., and delegates parts of the work to the policies.
The trick is to know what goes into the main class, and what policies should one create. Andrei's above mentioned excellent article gives us the clue: wherever we would need to make a possible limiting design decision, we should postpone that decision, we should delegate it to an appropriately named policy.
Policy classes can contain implementation, type definitions and so forth. Basically, the designer of the main template class will define what the policy classes should provide, what customization points they need to implement.
As we go by the analysis in policy-based design, it is a delicate task to create a good set of policies, just the right number. As little as necessary, but not less. The different customization points, which belong together, should go into one policy argument, such as storage policy, validation policy and so forth. A good rule of thumb during design is that you should be able to give a name to your policy, which represents a concept, and not one which represent an operation or some really tiny implementation detail. Persistence policy seems to be a good choice, while how to save policy does not.
As you do your policy-based design you will see how many other techniques will be useful, even if changed a bit, during your work. One example is that the template method pattern can be reinterpreted for compile time; so that your main class has a "skeleton" algorithm, which - at customization points - calls the appropriate functions of some the policies. You will also find yourself in using your policy classes as traits are used, asking type information, delegating type related tasks to it, a storage policy is one example where it can happen.
Simple example
Presented below is a simple example code written in [[C++]]. Before we explore the code, please note some common terminology :
- A policy class
- A C++ implementation of a policy.
- A policy-based class or host or host class
- A templated C++ class which uses one or more policy classes as template parameters.
Synopsis
Defined in the sample listing are :
- One policy-based class (CPolicyBasedClass).
- 3 policy classes (PolicyA, PolicyB and PolicyC).
The CPolicyBasedClass class exposes the PerformSomeOperation() public method which is called by its client code.
Inside PerformSomeOperation(), CPolicyBasedClass delegates a specialized process (encapsulated by the Action() method) to the selected policy class.
#include <iostream> struct PolicyA { static int Action() { std::cout << "Action() by PolicyA." << std::endl; return 0; } }; struct PolicyB { static int Action() { std::cout << "Action() by PolicyB." << std::endl; return 0; } }; struct PolicyC { static int Action() { std::cout << "Action() by PolicyC." << std::endl; return 0; } }; // Note that PolicyA will be selected by default if "policy" is left unfilled. template <class policy = PolicyA> class CPolicyBasedClass { public : int PerformSomeOperation() { // Perform various activities... // Call upon the services of the selected policy class... policy::Action(); // Perform various activities... return 0; } }; int _tmain(int argc, _TCHAR* argv[]) { // Instantiate the CPolicyBasedClass class using PolicyB. CPolicyBasedClass<PolicyB> policy_based_class_object; // Call the PerformSomeOperation() method. // PerformSomeOperation() will use the services // of the PolicyB class. policy_based_class_object.PerformSomeOperation(); return 0; }
Program Output
As part of the output when the above program is run, the following line will be displayed on the console :
Action() by PolicyB.