Search code examples
c++oopdesign-patternsabstract-classabstract

How to add functionality from combination of different derived class?


I have multiple handlers derived from base Handler class which can do single data updates individually.

For eg.

class Handler {
public: 
  Data_t dbdata_; 
public:
  virtual void updateFlags() = 0; 
}

class AHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; }
}

class BHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagB = 1; }
}

class CHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagC = 1; }
}

Individual handlers are called based on input flags in request. If request contains multiple flags, then in this case I want to try to avoid creating additional 6 handlers individually like following.

class ACHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; dbdata_.flagC = 1; }
}

class ABCHandler: public Handler {
.....
public:
  void updateFlags() { dbdata_.flagA = 1; dbdata_.flagB = 1; dbdata_.flagC = 1 }
}

Main function code will be something similar to this.

void process(Request_t *request)
{
  Handler *handler;
  if (request->flagA)
    handler = new AHandler();
  else if (request->flagB)
    handler = new BHandler();
  ....
  ...
  handler->updateFlags();
}

Is there a better way to approach this problem, by re-writing how the handlers are connected to each other ?

Thanks in advance.


Solution

  • You may want to consider a policy-based class design. For this, we define both a variadic function template, execute(), and a class template, HandlerHolder, that inherits from Handler and overrides the updateFlags() member function:

    template<typename FlagUpdater, typename... FlagUpdaters>
    void execute(Data_t& data) {
        execute<FlagUpdater>(data);
        if constexpr (sizeof...(FlagUpdaters))
            execute<FlagUpdaters...>(data);
    }
    
    template<typename... FlagUpdaters>
    class HandlerHolder final: public Handler {
    public:
        void updateFlags() override {
            if constexpr (sizeof...(FlagUpdaters)) 
                execute<FlagUpdaters...>(dbdata_);
        }
    };
    

    To this variadic class template, HandlerHolder, you can pass classes (i.e., policies) as template arguments that are callables and set the proper flags. The function call operator (i.e., operator()) of these policy classes are called in its overridden member function updateFlags().

    You would then define the policy classes like:

    struct AFlagSetter {
        void operator()(Data_t& dbdata) const {
            dbdata.flagA = 1;
        }
    };
    
    struct BFlagSetter {
        void operator()(Data_t& dbdata) const {
            dbdata.flagB = 1;
        }
    };
    
    struct CFlagSetter {
        void operator()(Data_t& dbdata) const {
            dbdata.flagC = 1;
        }
    };
    

    Note that you can also easily define policies for clearing the flags, for example:

    struct CFlagClearer {
        void operator()(Data_t& dbdata) const {
            dbdata.flagC = 0;
        }
    };
    

    By means of type aliases you can introduce type names for the handlers you were looking for:

    using ACHandler = HandlerHolder<AFlagSetter, BFlagSetter>;
    using ABCHandler = HandlerHolder<AFlagSetter, BFlagSetter, CFlagSetter>;