6

Is there some design pattern for handling situation where class hierarchy constructors parameters force the most bottom classes to have too many parameters?

Ideally this would be in C++, if language matters. Consider following generic example:

class dependency1;
class dependency2;
class dependency3;

class ClassA
{
public:

    ClassA(dependency1* dep1) { }       
};

class ClassB : public ClassA
{
public:

    ClassB(dependency1* dep1, dependency2* dep2) 
      : ClassA(dep1){ }

};

class ClassC : public ClassB
{
public:

    ClassC(dependency1* dep1, dependency2* dep2, dependency3* dep3) 
      : ClassB(dep1, dep2) { }  
};

Each of the class in hierarchy adds its specific dependency, eventually leading to 3 parameters in last class ctor, even though the 2 parameters are only used to pass to base classes. You can imagine more complicated example with like 11 parameters in the last class...

I understand it might be difficult to answer this in general, but I would like to know how to approach this problem in general.

The only thing close to this I found is using Builder pattern for situation with too many parameters, but it does not seem to fit to this case (or i just don't see how it helps to simplify the class hierarchy).

Feel free to redirect me to duplicate answer, I was not able to find any, but it seems something that should have been already asked.

  • How about having a hierarchy of builders which matches your main class hierarchy? – Vaughn Cato Mar 26 '16 at 14:50
  • 4
    Composition over inheritance completely solves the problem you're having. Do not abuse inheritance, just because you can inherit something does not mean you should. Besides introducing strucural problems, inheritance usually leads to tighter coupling than composition as well. – Andy Mar 26 '16 at 14:56
2

My take is that if you have a class that directly depends on 11 things, it's probably doing way too much.

Now consider what happens if instead of a subclass, ClassC had a member of type ClassB which had a member of type ClassA. It looks like we still have the same problem except there's a difference this time. ClassC shouldn't be directly depending on ClassB; instead, it should take in (an interface around) ClassB as a parameter. Now ClassC no longer needs to take the dependencies of ClassB as additional parameters. Similarly for ClassB and ClassA. At this point, each class only takes in the dependencies it directly needs. This has the benefit of indicating how coupled and complex the class is.

If you are using a dependency injection container or manually wiring up the classes, the end result is each class is stand-alone. For example, each class could be compiled without the other classes even existing (if you've used interfaces [abstract base classes]). There are no hard dependencies. Building up the actual dependency graph is done by a top-level procedure; either automatically by a dependency injection container or manually.

Returning to subclassing, you simply shouldn't run into the problem you've described unless you've designed your code poorly. As I suggested above, if you are starting to run into such code, you can take indicator that you're doing something wrong and should refactor. Whether ClassC is a subclass seven levels deep or a top-level class is irrelevant. At the end of the day, ClassC requires all those dependencies which you should take as a warning sign. Personally, I would recommend avoiding deep hierarchies, but I want to make clear that the hierarchy is not the problem; the high number of dependencies is. In this case, C++ is not a language that allows you to parameterize a class by its super class (admittedly a fairly unusual feature), which provides another argument against using inheritance as opposed to composition as a code structuring mechanism. A super class is a hard dependency of the subclass.

2

One possibility is to use a hierarchy of builder-like objects:

class dependency1;
class dependency2;
class dependency3;

class ClassA {
  public:
    struct Builder {
      dependency1 *dep1 = 0;
    };

    ClassA(const Builder &builder) { ... }
};

class ClassB : public ClassA {
  public:
    struct Builder : ClassA::Builder {
      dependency2 *dep2 = 0;
    };

    ClassB(const Builder &builder) : ClassA(builder) { ... }
};


class ClassC : public ClassB {
  public:
    struct Builder : ClassB::Builder {
      dependency3 *dep3 = 0;
    };

    ClassC(const Builder &builder) : ClassB(builder) { ... }
};

And use it like this:

  ClassC::Builder builder;
  builder.dep1 = dep1;
  builder.dep2 = dep2;
  builder.dep3 = dep3;

  ClassC c(builder);

This doesn't use the typical Fluent Interface form of builder, because that would require some tricks to make it work with a hierarchy, which probably isn't worth it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.