inheritance vs. composition for testability

2019-02-02 04:50发布

问题:

While designing my objects I find composition to be a better choice from the perspective of testability. The reason being, I can mock parts of the composition structure if I need to, while running unit tests. This is not possible if I have an inheritance hierarchy.

I would like to know if others have also found this to be a reason to prefer composition. Also what other testability pitfalls did you get into because inheritance was used?

回答1:

I believe that the more you start to develop using design patterns, you'll find more and more often where composition is going to be favored over inheritance. I actually believe in the Head First: Design Patterns book that "Favor Composition Over Inheritance" is one of the primary design principles.

Your example of being able to mock up parts of the composition for testing is probably one of the best examples possible.

Edit: Although the basic principle in design patterns is to favor composition over inheritance, that doesn't mean that there aren't design patterns which utilize inheritance where it is needed. Another basic example is the decorator pattern, where you are coding towards an abstract superclass (although this is for type matching and not for implementing an "is-a" relationship).



回答2:

It's not an either-or situation. They're not competitors.

Inheritance is rather easy to unit test, also. However, it sometimes requires mock concrete classes to test an abstract superclass.

Inheritance can easily be used improperly. Some designs look like "is-a" situations, but aren't really -- they're more nuanced. Sometimes it's really "behaves-like" where you need some kind of composition (a Strategy, for example) to separate behavior from other attributes.



回答3:

The Gang of Four Design Patterns book is basically all about why to prefer composition over inheritance and offers many ways to do that. Some reasons:

  1. of classes increases the complexity of the code base

  2. In many newer languages, inheritance is limited to one class, while you can compose as much as you want
  3. Base classes cannot be changed at runtime (essentially the issue behind what you are running into).


回答4:

I think the biggest reason why composition is easier to test is that (implementation) inheritance tends to create very coupled classes that are more fragile (Fragile Base Class) and harder to test in isolation.

Inheritance definitely has its uses, but I find myself preferring composition over inheritance more and more often.



回答5:

"Favor object composition over class inheritance" is actually from GoF book. This conversation with Erich Gamma describes this idea from the book.

One important pattern that requires inheritance is a template method pattern. This pattern is widely used very handy so inheritance is here to stay. Another common pattern that would use inheritance is Composite pattern. The point I try to make is that don't discount inheritance at all, but I hope it's clear from just looking at so many common APIs anyways...