Favor Composition Over Inheritance Part 2 | Source: Coding Delight

And here is the second part of the article


Yesterday I wrote part one of my two-part series on why we should favor the technique of composition over inheritance. I began by looking at how a purely inheritance-based model quickly becomes unworkable as the properties and methods of base classes often lead to an inflexible design. Today, by contrast, I will look at solving the same problem by the use of composition. Specifically, we will look at how using interfaces for composition in C# allows for a highly flexible design.

The problem posed yesterday was to model the behavior of a car when a driver applies changes to it. I want to be able to track the angle of the wheels and the speed at which the wheels have been turned by the engine. The first car to model is the Toyota Corolla.

Yesterday, the first class that was designed was the BaseCar class, which contained a number of abstract methods which defined our behavior. It also contained our wheels. Today, instead, I am going to look at the behaviors of the car. It seems to me that there are two separate behaviors which are independent of each other. There is steering and there is driving (or accelerating). These behaviors will form the basis for our interface design. A car also has a manufacturer, so we will create an interface for that too.

There are a couple of ways that we could look at designing the car interface. We could say that an ICar inherits from both ISteering and IDriving, but instead I think we should say that an ICar has both an ISteering and IDriving. This seems to make the most sense.

Already we can see that simply by thinking about the components and how they logically relate to each other, we have created a different design from before. The interfaces help us think more abstractly than base classes would otherwise had. Now that we have designed our interfaces (and the astute reader might have noticed that the Wheel class has now become an IWheel interface too, though defining either is beyond the scope of these articles) we can get started on defining the functionality of our classes.

First we will create a TwoWheelDrive class which implements the IDriving interface.

Immediately it can be seen that this class can be used for both types of two-wheel drive car – front or rear. All we will have to do is pass either the front or rear wheels to it. Next up we’ll implement the two-wheel steering functionality in much the same way. Note that in this case, the steering class has to be a “front” steering class or a “rear” steering class, as each type of steering requires the wheels to turn in opposite directions to achieve the same outcome for the driver.

And next, my Toyota manufacturer class. I’ve implemented it as a singleton because that will be sufficient for this problem.

Finally, I can create my ToyotaCorolla class.

Now when the customer comes along and asks for the rear wheel drive sports edition, I can create the following class for them.

In fact, it is at this point that you can quite easily see that the only difference between ToyotaCorollas lies in the parameters that are passed into the driving constructor. A pattern is emerging. We now have the ability to do away entirely with our ToyotaCorolla class. The only difference is in our constructor parameters. What I can do instead is refactor my code and use constructor parameters to define our classes.

So as you can see, by using composition we have created a much more flexible design. We can reuse the bits that make sense to be reused and ignore the bits that don’t. The interfaces have helped us think abstractly and separated out how the objects relate to each other from how the objects work. We can use a car without knowing how the steering is implemented or whether it is a front, rear or four-wheel drive. We no longer have a complicated object hierarchy and adding new car designs takes a little effort. When it comes time to design a car with four-wheel drive, all we need to do is create a four-wheel drive class and a factory method.

Far too many professional developers think in an “is-a” mindset when they want to reuse code. I hope that I have sufficiently demonstrated that composition helps us reuse code far more efficiently and with a lot less complexity than using inheritance. As always, leave a comment, I would love to hear your feedback!

I am taking two weeks holiday, after which I will be (hopefully) blogging on a regular basis.

 

Source: Favor Composition Over Inheritance part 2 | Coding Delight


Also published on Medium.