Object-oriented – Handling Different Parameters for Derived Classes

api-designcdesign-patternsobject-oriented

I'm designing an API and I ended up having a few pure abstract classes. Because of the nature of the problem that I'm solving, each derived class has to be initialized with different sets of parameters. My solution was to basically define their constructors and pass the appropriate parameters when I'm making each class using the factory method. This makes the factory method rather complicated. Since I have to pass all possible parameters and then filter the appropriate parameters for each derived class and pass it around. It also opens up a path for error.

In order to avoid this complication, I would like to have the parameters as a type. So, I'm thinking to define struct BaseClassParams and basically include everything that derived classes need into it and simply pass the param to their constructor. This achieve what I want to do but since parameters of each derived class might differ from one to another, I want a better encapsulation. I thought doing something this:

class Base {

public:
     struct BaseClassParams {
          struct DerivedClass_A_Params {

          };
          struct DerivedClass_B_Params {

          };
     };
};

This way, I can encapsulate everything more neatly and in the factory method, only pass the appropriate parameter section to the derived class constructor. I wonder if you have faced this issue and if you have any suggestions? Overall what do you think of this approach and problem.

Edit: To clarify a bit, these derived classes are going to be used by a class, i.e., Manager. The Manager can choose to create different derived class, let's call them Employee, with different parameters. The Manager class can be initialized using a config file, i.e., JSON, or directly. If the Manager is created directly via the API, then Manager::Builder class can choose to create as many different employees with different parameters. So, I need to create these objects at run time.

P.S. I found this question and answer. The difference here is that my derived classes are not necessarily sharing parameters, they might have completely different parameters. This is kind of relevant too.

Best Answer

Problem with BaseClassParams

Using

 struct BaseClassParams {
      struct DerivedClass_A_Params {
      };

      struct DerivedClass_B_Params {
      };
 };

to capture the data needed to construct derived classes is DOA in my book. As soon as you need DerivedClass_C, you'll have to go back to the base class and update it to

 struct BaseClassParams {
      struct DerivedClass_A_Params {
      };

      struct DerivedClass_B_Params {
      };

      struct DerivedClass_C_Params {
      };
 };

That violates The Open-Closed Principle and should be avoided.

Separation of concerns

  1. Make sure that Base and its derived classes are clean. Don't pollute them with what mechanisms are used to construct them.

    Say your base class is Shape, and Circle and Square are derived from it.

    • It makes no sense to pollute Shape with Circle or Square specific data.
    • It also makes no sense to pollute Circle and Square with higher level concerns -- such as how does the user gather the data to construct them.
  2. Using a factory to construct any derived type of a given base type implies the ability to capture derived class specific data in a derived class agnostic manner. You'll have to find a generic way of capturing such data. The can be:

    • std::map<std::string, std::string>,
    • std::vector<std::string>,
    • A JSON object, etc.
  3. You will need a set of classes/functions to bridge the gap between (1) and (2) above. That's were you need concrete factories that are able to:

    • Take the generic data from (2)
    • Validate the data for constructing the specific object type they are responsible for constructing.
    • Construct objects of the derived type.

My suggestion in pictorial form

The image below illustrates my suggestion.

enter image description here

Related Topic