C++: Multiple Policies calling each other

2019-08-29 07:17发布

问题:

For a policy-based class-design I need some of the policies to call functions that are found in other policies:

struct PolicyA {
  void foo() {
    // ...
  }
};

struct PolicyB {
  void bar() {
    // Need to call foo() here
  }
};

template <class A, class B> MyClass : A, B {};

The two options I see are:

  • Pass MyClass<A, B> to PolicyA and PolicyB as template parameters and do something like dynamic_cast<MyClass<A, B>*>(this)->foo();. However this may easily get very complicated (especially if it's not only B calling A but also the other way).
  • Have a base class for each policy where all the functions are declared as pure virtual. This fails when one of the function needs its own template parameters, since virtual template functions are not possible.

This should be a common problem, but I didn't find anything. I guess there probably is some boost-magic or something. How would you solve this?

Edit: See also the followup here.

回答1:

Why doesn't PolicyB encapsulate PolicyA and call it as it needs? PolicyB is obviously aware of PolicyA - no need for base classes or inheritance.

It seems like all of your Policy implementations have some common apply method that outside callers can agree upon?

Edit:

Right, so if it's not just a simple case of one particular Policy implementation needing access to another, you should probably focus on creating a single interface for Policy - perhaps even a Rule class that can be composed together to form a particular Policy - see the Specification pattern.

class Rule {
    allows(Foo foo);
}

class Policy {
    Rule rule = new NotNullRule().and(someOtherRule);

    void applyTo(Foo foo) {
        if (rule.allows(foo)) {
            return foo;
        }
        foo.disable();
    }
}

Call them whatever you want, Condition, Rule, Specification - they're useful for assembling pieces of logic together in a manner that doesn't depend on them. Then when someone is looking at PolicyA they can see, clearly, the rules/conditions that govern its behavior, objectified.

Once you have policy implementations in place, if you need to enforce several policies on something, you can use the composite pattern again so that external classes only see a Policy interface. But again, you have a clear 'named' policies that are being referred to, not just cross-calling functions.