Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing C++ classes with partly common implementations

I am designing a C++ module. This module can receive 3 different types of requests: Request-A, Request-B and Request-C.
For each type, I have a corresponding handler class: RequestHandler-A, RequestHandler-B and RequestHandler-C (all of these implement the IRequestHandler interface).
Each handler has to carry out certain actions to fulfill its request.
For example, RequestHandler-A needs to perform these in sequence:
Action-1
Action-2
Action-3
Action-4
Action-5

RequestHandler-B needs to perform these in sequence:
Action-1
Action-3
Action-5

RequestHandler-C needs to perform these in sequence:
Action-4
Action-5

The result of one action is used by the next one.

I am struggling to design these classes so that common action implementations are reused across handlers. Are there any design patterns that can be applied here? Maybe Template method pattern could be a possibility but I am not sure. Any suggestions would be greatly appreciated.

PS: to make things more interesting, there is also a requirement where, if Action-2 fails, we should retry it with different data. But maybe I am thinking too far ahead.

like image 972
oyenamit Avatar asked Jul 23 '26 14:07

oyenamit


2 Answers

"Common implementations" means that your solution does not have anything to do with inheritance. Inheritance is for interface reuse, not implementation reuse.

You find that you have common code, just use shared functions:

void action1();
void action2();
void action3();
void action4();
void action5();

struct RequestHandlerA : IRequestHandler {
    virtual void handle( Request *r ) {
        action1();
        action2();
        action3();
    }
};

struct RequestHandlerB : IRequestHandler {
    virtual void handle( Request *r ) {
        action2();
        action3();
        action4();
    }
};

struct RequestHandlerC : IRequestHandler {
    virtual void handle( Request *r ) {
        action3();
        action4();
        action5();
    }
};

Assuming that the common function are just internal helpers, you probably want to make them static (or use an anonymous namespace) to get internal linkage.

like image 188
Frerich Raabe Avatar answered Jul 26 '26 04:07

Frerich Raabe


Are you looking for something like this?

#include <iostream>

using namespace std;

class Interface{
    public:
        void exec(){
            //prepare things up
            vExec();
            //check everything is ok
        };
        virtual ~Interface(){}
    protected:
        virtual void vExec() = 0;
        virtual void Action0() = 0;
        virtual void Action1(){}
        void Action2(){}
};

void Interface::Action0(){
}

void Action3(){}

class HandlerA : public Interface{
    protected:
        virtual void vExec(){
            Action0();
            Action1();
            Action3();
        }
        virtual void Action0(){
        }
};

class HandlerB : public Interface{
    protected:
        virtual void vExec(){
            Action0();
            Action1();
            Action2();
            Action3();
        }
        virtual void Action0(){
            Interface::Action0();
        }
};

int main()
{
    Interface* handler = new HandlerA();
    handler->exec();
    HandlerB b;
    b.exec();

    delete handler;
}

As you can see the actions can be virtual members, non-virtual members, free functions, or whatever you might think of, depending on what you need.

The "additional" feature of feeding the actions with different data can be performed in exec() (if it is generic) or in vExec (if it is handler specific). If you give us more details I can modify the example accordingly.

Also, you can make vExec public and get rid of exec. The one in the example is just a practice I like most (making interface non-virtual and virtual functions non-public).

like image 37
Stefano Falasca Avatar answered Jul 26 '26 04:07

Stefano Falasca