Replacing Inheritance with Composition
Quite a while back, I posted about how, despite the fact that you should prefer composition over inheritance, you can best design classes for inheritance. Now, I wish to give some examples of how you can take your code that uses inheritance and change it to use composition instead, which will often actually make your code more flexible.
The code will be in java, but the concepts can transferred to any language that is object-oriented. Some languages might have constructs that make parts of this easier, too (Kotlin’s Delegates).
One thing is important to remember, though: Even if you switch “completely” to composition, you will still have some inheritance. This inheritance will be from interfaces and interface-like classes only, though. It barely counts as inheritance, really, since all it’s doing is restricting itself to an API and not inheriting implementation details.
Basic Composition: The Delegate Pattern
The Delegate Pattern isn’t a very strictly-defined pattern, and you could even say that it is simply a synonym for composition. While the Adapter Pattern certainly qualifies as composition and definitely delegates to the wrapped object, I don’t consider it the Delegate Pattern. Some do, and that’s fine, but I prefer a more rigid definition where the delegate and wrapper implement the same interface. This is still fits most composition-based “patterns”, but it ignores the less obvious cases.
Well, let’s start with an interface that our code is going to be using. We’ll be using Foo
as the interface.
public interface Foo { void bar(); int baz(int i); }
Now we need a base implementation that we’ll be trying to inherit from before converting the relationship to composition instead. We’ll call it BasicFoo
.
public class BasicFoo implements Foo { public void bar() { System.out.println("method one called"); } public int baz(int i) { return i * 2; } }
Lastly, we need a class that inherits from BasicFoo
. We’ll call it ComplexFoo
.
public class ComplexFoo extends BasicFoo { @Override public int baz(int i) { return super.baz(i) * 8; } }
For the most part, ComplexFoo
just inherits the implementation of BasicFoo
, but it does make a verification of incoming arguments on baz()
.
So, what would it look like to change ComplexFoo
to use composition instead of inheritance? Like this!
public class ComplexFoo implements Foo { private Foo wrapped; public ComplexFoo(Foo wrapped) { this.wrapped = wrapped; } public void bar() { wrapped.bar(); } public int baz(int i) { return wrapped.baz(i) * 8; } }
Instead of inheriting from BasicFoo
, ComplexFoo
only inherits from the interface, Foo
. In order to get access a base functionality, such as BasicFoo
‘s, it accepts a Foo
object in its constructor, and then delegates all the methods to that object, putting in the extra functionality before or after the delegation call, just you would if you were “delegating” via super
.
Restricting the Delegate
What if you only wanted ComplexFoo
to only be able to “extend” BasicFoo
and no other base implementations? First off, this is very restrictive, so it probably shouldn’t be the case. But, if you have good reasons to go that route, you’ve got several options.
The first option is to only accept BasicFoo
s in the constructor. If BasicFoo
is final
(cannot be extended; this is actually very recommended, especially if you mostly do composition), this works, but if it’s not, you may end up with something that inherits from BasicFoo
instead an actual BasicFoo
object.
The second option is to hardcode it to make a BasicFoo
in the constructor. Simply by seeing the word “hardcode”, you knew that this is not a recommended procedure.
My last option is to make the constructor private
or package-private, still accepting any Foo
object, and have a static factory method create the BasicFoo
instance and pass it into the constructor. This way, you can pass in a test double of Foo
in your tests if BasicFoo
could be problematic for tests.
Replacing the Template Pattern
The simplest way to replace the Template Pattern is to use the Strategy Pattern instead. Often, I like to think that a Strategy object only has one public method, but that’s largely because it allows you to transform it to a lambda or method reference more easily. In truth, the Strategy Pattern allows the Strategy objects to implement many methods to fulfill its strategy interface. You may not suffer from this view of the pattern, but I thought I’d share it with you, just in case.
I suppose you would like an example, huh?
public abstract class Bar { public int foo(int i) { i = bar(i); i = baz(i); return qux(i); } abstract protected int bar(int i); abstract protected int baz(int i); abstract protected int qux(int i); }
Bar
uses the Template Pattern, leaving some methods protected
so that subclasses fill them in to be used by Bar
‘s public
methods. I hope I don’t have to give you an example subclass; if you’re confused about what the Template Pattern is, please look it up.
Let’s transform this Template Pattern into the Strategy Pattern:
public class Bar { private BarStrategy strategy; public Bar(BarStrategy strategy) { this.strategy = strategy; } public int foo(int i) { i = strategy.bar(i); i = strategy.baz(i); return strategy.qux(i); } }
Our new Bar
class no longer has those protected
methods to deal with. Instead, it accepts a BarStrategy
and delegates to it for those calls.
I originally had a real life example of this idea, but then I realized that it was the Strategy Pattern. After that, I thought that any example wouldn’t really be all that helpful, since any example of the Strategy Pattern is an example of a compositionally engineered Template Pattern.
The Gains
So, what do we gain from shifting away from inheritance and towards composition? Well, we gain more code, since we often have to replace implicit delegation to the “super” class with explicit delegation calls (some languages will provide shortcuts to get around this, such as Kotlin’s Degelates). Yes, it’s a gain, but not a good one. What about real helpfulness?
The biggest thing that’s helpful about switching to composition instead of inheritance is the lack of what I like to call “combination hell”. This is the biggest problem that the Decorator Pattern was designed to get around, but any switch to composition helps with this. You come up with a class extension that provides a certain extra bit of functionality around the base functionality. Then you come up with another one with a different bit of functionality. Eventually, you realize you want a class with both. If you did this with inheritance, you’d need to create another class that combines them somehow. If you used composition, you have everything you need, since you can just have the one extension accept, wrap, and delegate to the other (which wraps the base class).
Another large benefit of using composition was mentioned earlier: testability. Now you can test just the new functionality of the class by making a dud of a test double to replace the “super” class. This testability is courtesy of following the SRP better.
Unfortunately, using composition will usually make creation of the objects a hair more difficult, needing to create instances of each intermediate object instead of just the specific subclass, but seeing as most creation should be done in some sort of Factory context, it’s not a problem you have to deal with a lot.
Avoiding Some Explicitness
I mentioned up above that composition requires a bit more explicitness to it than inheritance, since it requires you to fill in all the methods with delegation calls. I mentioned that some languages provide features that let you shortcut around this, but you can get around this in languages without such features if you’re willing to stretch my meaning of “interface-like classes” that I used above. Since my favorite pattern, the Decorator pattern, uses it, I’m willing to stretch it.
I’m referring to the creation of an abstract class whose whole purpose is to delegate. This is the Decorator
base class in the Decorator pattern. It’s a fully fleshed-out – yet abstract – class that technically does nothing, since it delegates all of its calls to the wrapped object. I have no qualms with the creation of such a class to be inherited from. Since it essentially does nothing, it can be looked at as “interface-like”. The really nice thing about this is that you can inherit from it, and if you’re not making any changes to a method, you don’t need to include it.
Not only does this lead to shorter code, but it also follows the SRP better than composition was already doing: one class is used for explicit delegation, the other ones for ONLY adding the new functionality (and explicit delegation when needed). It also removes code duplication. It’s definitely worth the inheritance “coupling”.
Outro
That’s all I can give you about the wonderful world of “composition over inheritance”. There may still be some times when inheritance is still the best option, but I urge you to strongly consider composition before making that decision.
Reference: | Replacing Inheritance with Composition from our JCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |
Good article, such programming principle exists “prefer composition over inheritance”. Though, 1st we need to consider the purpose of inheritance – acquiring common characteristics and behavior, this is useful for building a hierarchy (mentioned interface-like classes also), trees. Pretty straightforward examples – animal, vehicle etc, you cannot avoid inheritance overall and not because of just replacing with composition requires some interface-like classes. One more principle to be equipped with is “LSP – Liskov substitution principle” to check if inheritance can take place in particular case.
While it’s true that, other than what I mentioned in my article, you can’t fully avoid inheritance, you’d be surprised how often it turns out to still be a better idea to use composition instead.
Your understanding of LSP seems like it might be a little narrow, since it applies to using composition as well, since it’s inheritance from an interface.
I’d not say narrow understanding of LSP as it states mainly about subtypes, i.e. in other words inheritance, of course composition may use inheritance as it is written by using interfaces/classes.