简体   繁体   中英

Design for generic options to algorithms

I want to know if there is a design pattern for specifying options to a set of algorithms. I am using C++. Let me describe my problem. I am having a set of algorithms and these algorithms have different options. I want to design a single point access to these algorithms. Something similar to a strategy pattern. This single point access is a controller class which takes input as a generic options class. Depending upon the options, a suitable algorithm will be used. I want to generalize these options so that i can extend algorithms and the client. Thanks, Amol

An often-used pattern is to create a policy class which is passed as a template type to your class and then inherited from privately:

template <typename Policy>
class fancy_algorithm : private Policy {
};

One famous example of this is the std::allocator class which is often inherited in that manner:

template <typename T, typename Alloc = std::allocator<T> >
class my_container : private Alloc {
public:
    typedef Alloc allocator;

    // It's necessary to make the base class names available explicitly.
    typedef typename allocator::pointer pointer;

    using allocator::construct;
    // …
};

I agree with Konrad on policy-based design. I also recommend Modern C++ Design: Generic Programming and Design Patterns Applied . After this book your vision of C++ will be changed forever ;)

Building on Konrad's suggestion of using policy types , if your algorithms require parameters at construction time, you can handle this cleanly by requiring that any Policy class has a nested type called Params , and then provide a constructor inside fancy_algorithm<Policy> that takes an argument of this type and passes it to the contained Policy object:

template <typename Policy>
class fancy_algorithm : private Policy {
public:
    typedef typename Policy::Params Params;    // Need to redeclare :(

    explicit fancy_algorithm(Params params = Params()) : Policy(params) {}
};

Any relevant parameters need to be packaged into a single object of type Policy::Params .

The Policy class is always constructed with a single argument of type Policy::Params . To work with policy classes that (may) require no parameters, provide a default constructor (or use the implicitly declared one) in Params , not in Policy . This way, by using a default value for the fancy_algorithm<Policy> constructor as above, we enable convenient default-construction of a fancy_algorithm<Policy> whenever Policy::Params has a default constructor (ie when the Policy doesn't require any parameters). No safety is lost: if Policy::Params lacks a default constructor (indicating that some parameters are required ), any attempt to default-construct a fancy_algorithm<Policy> object will fail at compile time.

Example:

struct multiply_by_params {
    multiply_by_params(int x /* = 42 */) : _x(x) {}     // See bottom
    int get() const { return _x; }    // Or, just make multiply_by a friend

private:
    int _x;
};

struct multiply_by {
    typedef multiply_by_params Params;
    multiply_by(Params p) : _x(p.get()) { /* Other initialisation */ }

    // Other code implementing the strategy (e.g. an operator()())
    ...

private:
    int _x;
};

fancy_algorithm<multiply_by> a(69); // Always compiles
fancy_algorithm<multiply_by> b;     // Compiles iff /* = 42 */ is uncommented

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM