Search code examples
c++oopclass-design

How to design a class with methods which have similar functions but different signatures


The task I have at hand is to write a system to test out n different algorithms in a library - all doing the same broad task but with different methodologies. I thought about creating a main class to which I send an argument defining the algorithm to be used. This main class in-turn calls the taskAlgorithm class passing on the parameter to be used and the required input parameters for the algorithm to be used. The taskAlgorithm class should then instantiate the specific algorithm class to be used in its constructor. taskAlgorithm::Process should run the algorithm and store the result and taskAlgorithm::Result should return the result.

I am stuck on figuring out how to write this taskAlgorithm class. In the constructor of this class, based on the algorithmCode parameter I want to instantiate an object of the specific algorithm, however, all algorithm classes are different and do not necessarily have a common base class. What's the best way to tackle this problem?

Specifically in

taskAlgorithm::taskAlgorithm(int algorithmCode, int argument1){
   if(algorithmCode == 1){
      //instantiate the algorithm1 object
   }
   else if(algorithmCode == 2){
      //instantiate algorithm2 object
   }
}

how should I instantiate objects for each of different algorithm classes if they don't necessarily share a common base class?


Solution

  • If they are related algorithms, perhaps you can think of the "common" interface that, if it existed, would be capable of calling into any of the algorithms if you knew which parts to use, or which transformations to perform.

    This would allow you to write a wrapper interface to all the algorithms, so they can be called uniformly. Each wrapper could "know" how to take its inputs to call the underlying algorithm that it wraps. I didn't do it here, but you could do the same for the return value.

    For example Algorithm A is a sort taking a pair of iterators into a vector of ints, and a comparator, while Algorithm B is a different kind of sort, taking a pointer to an int array, a length, and a comparator.

    class Algo {
    public:
        using Iter = std::vector<int>::iterator;
        using Compare = ...;
        virtual void call(Iter b, Iter e, Compare comp) = 0;
        virtual ~Algo() = default;
    };
    
    class A : public Algo {
        void call(Iter b, Iter e, Compare comp) override {
          algorithmA(b, e, comp);
        }
    };
    
    class B : public Algo {
        void call(Iter b, Iter e, Compare comp) override {
            if (b != e) {
                algorithmB(&*b, std::distance(b, e), comp);
            }
        }
    };
    

    Now you can wrap algorithm A with class A, and algorithm b with class B, and they are invoked identically, and the wrappers share a common base class so they can be used polymorphicly.

    Of course, you can't templatize virtual functions so the iterator type is fixed, but the example here is about making a wrapper hierarchy that normalizes the interface to the algorithms. Then you can avoid a nasty switch statement (which requires editing working code every time you add a new algorithm.) With a class wrapper approach, just wrap new algorithm with a new wrapper class and it should just fit into any system that already works with the existing algorithms (provided you don't change the signature of the virtual call() function).

    This isn't exactly answering your question but I think it's close enough to maybe be useful. :)