Policy-based design
1、在阅读boost Generic Programming Techniques#Policy Classes时,发现的这个policy-based design。
2、policy-based design遵循"program to an abstraction and polymorphism"
wikipedia Modern C++ Design#Policy-based design
Policy-based design, also known as policy-based class design or policy-based programming, is the term used in Modern C++ Design for a design approach based on an idiom for C++ known as policies. It has been described as a compile-time variant of the strategy pattern, and has connections with C++ template metaprogramming. It was first popularized in C++ by Andrei Alexandrescu with Modern C++ Design and with his column Generic<Programming>
in the C/C++ Users Journal, and it is currently closely associated with C++ and D as it requires a compiler with highly robust support for templates, which was not common before about 2003.
NOTE: static polymorphism
The central idiom in policy-based design is a class template (called the host class), taking several type parameters as input, which are instantiated with types selected by the user (called policy classes), each implementing a particular implicit interface (called a policy), and encapsulating some orthogonal (or mostly orthogonal) aspect of the behavior of the instantiated host class. By supplying a host class combined with a set of different, canned(表明意思是: 听装的,引申意思: 封装的) implementations for each policy, a library or module can support an exponential number of different behavior combinations, resolved at compile time, and selected by mixing and matching the different supplied policy classes in the instantiation of the host class template. Additionally, by writing a custom implementation of a given policy, a policy-based library can be used in situations requiring behaviors unforeseen by the library implementor. Even in cases where no more than one implementation of each policy will ever be used, decomposing a class into policies can aid the design process, by increasing modularity and highlighting exactly where orthogonal design decisions have been made.
NOTE: 上述"combinations"其实对应的就是N * M code reuse。
While assembling software components out of interchangeable modules is a far from new concept, policy-based design represents an innovation(创新) in the way it applies that concept at the (relatively low) level of defining the behavior of an individual class.
NOTE: 上面这段话的表明意思是: 虽然从可互换模块中组装软件组件远非一个新概念,但基于策略的设计代表了一种创新,即在定义单个类行为的(相对较低的)级别上应用这个概念。
policy-based design也是基于"decompose and assemble",它的创新点在于"policy class",policy class是at the (relatively low) level的。
decompose and assemble,它们是相反的过程。
behavior and interface: 使用interface来描述behavior
Policy classes VS callback
Policy classes have some similarity to callbacks, but differ in that, rather than consisting of a single function, a policy class will typically contain several related functions (methods), often combined with state variables or other facilities such as nested types. A policy-based host class can be thought of as a type of metafunction, taking a set of behaviors represented by types as input, and returning as output a type representing the result of combining those behaviors into a functioning whole. (Unlike MPL metafunctions, however, the output is usually represented by the instantiated host class itself, rather than a nested output type.)
Inheritance and composition
A key feature of the policy idiom is that, usually (though it is not strictly necessary), the host class will derive from (make itself a child class of) each of its policy classes using (public) multiple inheritance. (Alternatives are for the host class to merely contain a member variable of each policy class type(composition), or else to inherit the policy classes privately; however inheriting the policy classes publicly has the major advantage that a policy class can add new methods, inherited by the instantiated host class and accessible to its users, which the host class itself need not even know about.)
NOTE:
一、
1、之前在实现AMUST API的时候,使用的是multiple inheritance。
2、libstdc++ hash table的实现,就是典型的使用policy based design + multiple inheritance,参见:
https://gcc.gnu.org/onlinedocs/gcc-6.4.0/libstdc++/api/a01320_source.html
https://code.woboq.org/gcc/libstdc++-v3/include/bits/hashtable_policy.h.html
二、上面这段话中涉及了composition和inheritance,它让我想到了"composition over inheritance"。
三、policy-based design中,inheritance、composition的目的是: code reuse
四、"policy based design-multiple inheritance-composition-assemble ability"
A notable feature of this aspect of the policy idiom is that, relative to object-oriented programming, policies invert(颠倒) the relationship between base class and derived class - whereas in OOP interfaces are traditionally represented by (abstract) base classes and implementations of interfaces by derived classes, in policy-based design the derived (host) class represents the interfaces and the base (policy) classes implement them. In the case of policies, the public inheritance does not represent an is-a relationship between the host and the policy classes. While this would traditionally be considered evidence of a design defect(缺点) in OOP contexts, this doesn't apply in the context of the policy idiom.
NOTE: 上面这段话其实所描述的topic是"Subtyping-VS-inheritance",在
Theory\Programming-paradigm\Object-oriented-programming\Subtyping-polymorphism\Subtyping-VS-inheritance
中对它进行了专门的描述;显然,policy-based design所使用的是inheritance,而非subtyping。
A disadvantage of policy
A disadvantage of policies in their current incarnation is that the policy interface doesn't have a direct, explicit representation in code, but rather is defined implicitly, via duck typing, and must be documented separately and manually, in comments. 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 trick is to know what goes into the main class, and what policies should one create. The article mentioned above gives the following answer: 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.
NOTE: 上面这段话虽然短,但是观点/知识是比较密集的,下面逐个进行梳理:
1) Template is behavior-based,参见
C++\Language-reference\Template\Programming-paradigm\Generic-programming\Template-is-behavior-based
章节2) Explicit VS implict: "policy interface doesn't have a direct, explicit representation in code, but rather is defined implicitly, via duck typing"
上述**fixed implementation**对应的是commonality,它需要"goes into the main class",**interface, the policy-based class**对应的是variability,它需要由policy来实现。
3) Commonality-variability analysis: 参见相关章节
4) 上述**fixed implementation**对应的是commonality,它需要"goes into the main class",**interface, the policy-based class**对应的是variability,它需要由policy来实现。
显然,policy-based design也是遵循"program to an abstraction and polymorphism"原则的。
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.
It may be a delicate task to create a good set of policies, just the right number (e.g., the minimum necessary). The different customization points, which belong together, should go into one policy argument, such as storage policy, validation policy and so forth. Graphic designers are able to give a name to their policies, which represent concepts, and not those which represent operations or minor implementation details.
Policy-based design may incorporate other useful techniques. For example, the template method pattern can be reinterpreted for compile time, so that a main class has a skeleton algorithm, which – at customization points – calls the appropriate functions of some of the policies.
This will be achieved dynamically by concepts[3] in future versions of C++.
Simple example
Presented below is a simple (contrived) example of a C++ hello world program, where the text to be printed and the method of printing it are decomposed using policies. In this example, HelloWorld is a host class where it takes two policies, one for specifying how a message should be shown and the other for the actual message being printed. Note that the generic implementation is in Run
and therefore the code is unable to be compiled unless both policies (Print
and Message
) are provided.
#include <iostream>
#include <string>
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy {
public:
// Behavior method.
void Run() const {
// Two policy methods.
Print(Message());
}
private:
using LanguagePolicy::Message;
using OutputPolicy::Print;
};
class OutputPolicyWriteToCout {
protected:
template <typename MessageType>
void Print(MessageType&& message) const {
std::cout << message << std::endl;
}
};
class LanguagePolicyEnglish {
protected:
std::string Message() const { return "Hello, World!"; }
};
class LanguagePolicyGerman {
protected:
std::string Message() const { return "Hallo Welt!"; }
};
int main() {
// Example 1
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>
HelloWorldEnglish;
HelloWorldEnglish hello_world;
hello_world.Run(); // Prints "Hello, World!".
// Example 2
// Does the same, but uses another language policy.
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>
HelloWorldGerman;
HelloWorldGerman hello_world2;
hello_world2.Run(); // Prints "Hallo Welt!".
}
Designers can easily write more OutputPolicy
s by adding new classes with the member function Print
and take those as new OutputPolicy
s.
drdobbs Policy-Based Design in the Real World
Trait and policy
stackoverflow What is the difference between a trait and a policy?
stackoverflow What is the point of STL Character Traits?
etutorials 8.4 Traits and Policies