简体   繁体   中英

How can I combine generic iterator-based algorithms with implementation based algorithms?

I am using the Strategy Pattern, together with the Abstract Factory Pattern to generate different algorithms in a Calculator class during run-time.

The calculations will depend on a Relationship between involved types. This is why I made the "*Algorithm::calculate" a member function template, generic with respect to a Relationship.

However, I already have an algorithm that is completely implementation-based in the existing code, it is not-generic nor iterator-based, and I want to add it to the algorithm hierarchy so that I can produce it using the AbstractFactory as well and see how it behaves.

An implemenation based algorithm uses the member functions of the types involved in the calculations to get the calculation done. In this example, it would use RelationshipWithA::target_type member functions to access the data of the Type&, as well as "A" member functions to access the data of RelationshipWithA::a_.

This is what I came up with so far (this is just a model, without the Abstract Factory, and the Calculator class):

#include <iostream>

class Result{}; 

class A {};  

class B {
    public: 
        void specific() const 
        { 
            std::cout << "B::specific()" << std::endl;
        };
};

class C : public B {};

class D {};

template<class Type>
class RelationshipWithA
{
    const A& a_; 

    const Type& t_;

    public: 
        typedef Type target_type; 

        RelationshipWithA (const A& a, const Type& t)
            :
                a_(a), 
                t_(t)

        {
            std::cout << "RelationshipWithA::ctor" << std::endl;
        }; 

        const A& a() const 
        {
            return a_; 
        }

        const Type& type() const
        {
            return t_;
        }
};

class DefaultAlgorithm 
{
    public:
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            std::cout << "DefaultAlgorithm::calculate" << std::endl;
            const A& a = r.a(); 
            const typename Relationship::target_type& t = r.type(); 
            // Default iterator based calculation on a, target_type and r
        };
};

class AlternativeAlgorithm 
: 
    public DefaultAlgorithm 
{
    public:
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            std::cout << "AlternativeAlgorithm::calculate" << std::endl;
            // Optimized iterator based calculation on a, target_type and r
        }
};

class ImplementationBasedAlgorithm 
:
    public DefaultAlgorithm 
{
    public:
        // No specialization: Relationships store
        // a const reference to any class that inherits from B
        template <class Relationship>
        void calculate (Result& res, const Relationship& r)
        {
            // Use B implementation and the Relationship With  A to compute the result
            std::cout << "ImplementationBasedAlgorithm::calculate" << std::endl;
            const A& a = r.a(); 
            const B& b = r.type();
            b.specific();
            // Implementation based on B implementation
        }
};

int main(int argc, const char *argv[])
{
    Result res; 

    A a; 
    C c; 

    RelationshipWithA<C> relationshipAC (a, c);

    DefaultAlgorithm defaultAlg; 
    AlternativeAlgorithm alternativeAlg;
    ImplementationBasedAlgorithm implementationAlg;

    defaultAlg.calculate(res, relationshipAC);
    alternativeAlg.calculate(res, relationshipAC);
    implementationAlg.calculate(res,relationshipAC);

    D d; 
    RelationshipWithA<D> relationshipAD (a, d);

    defaultAlg.calculate(res, relationshipAD);
    alternativeAlg.calculate(res, relationshipAD);
    // This fails, as expected
    //implementationAlg.calculate(res,relationshipAD);

    return 0;
}

I like this design because the algorithms are not generic classes, which makes it easy for the Generic Abstract Factory to produce them during run-time.

However, in Effective C++ there is an Item 36 saying: "never redefine an inherited non-virtual function". I mean, non-virtual functions are implementation invariant, they should not be overriden in general, but:

  1. There are no virtual member function templates available in C++.
  2. If I make the Algorithm classes generic on RelationshipWithA and "*Algorithm::calculate" a vritual member function, the Factory needs to know about the Realtionship in order to generate Algorithms, and the code gets seriously smelly (to me at least).

Is this then a proper solution for the problem, even though I override inherited non-virtual functions (function templates)?

To the client, there is no difference in behaviour what so ever: the result is there, the only difference is in the way it is computed. This means that the Is-A relationship is still upheld: the "*Algorithm::calculate" is still implementation invariant to the client.

It isn't really an Is-A relationship...

The specific implementations aren't really A DefaultAlgorithm ... they are specific algorithms...

You could have an empty BaseAlgorithm class that you can create with the factory. But then you'll need to cast it to the right type anyway before using the template functions. This kinda beats the factory pattern anyway, because you aren't using an interface.

In your case if the factory creates one of the derived classes but returns the base class, if you use that variable, it will call the base class methods:

DefaultAlgorithm algo = Factory.CreateImplementationBasedAlgorithm();
RelationshipWithA<D> relationshipAD (a, d);
algo.calculate(res, relationshipAD); //won't fail because the base class methods are used (because it isn't virtual)

To fix that, you could make the a base Relationship class, and make the calculate() method virtual.
The calculate() method will get then you could static_cast the Relationship to some base_relationship interface that has the interface you want for that algorithm, and so you can achive the compilation failure for not having the right a() or type() methods.

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