The Flaws of Inheritance - 2022
Details
Title : The Flaws of Inheritance Author(s): CodeAesthetic Link(s) : https://www.youtube.com/watch?v=hxGOiiR9ZKg
Rough Notes
Suppose there is an (abstract) Image
class, which methods such as getPixelValue
,
flipHorizontal
etc. If we want to implement a save
and load
method,
this depends on the encoding so we need to implement these methods
separately for different image formats such as png, jpg, bmp etc. In
this case, we create a new class for each format which inherits from
the Image
class.
Now assume we want images where instead of loading them, we create
them within the code itself, lets call the class for this
DrawableImage
- if we inherit from Image
we also inherit the
(unimplemented) save
and load
. The solution could be to create a new
class FileImage
that has the save
and load
methods, but now we have to
do a lot of refactoring since now the save
and load
methods are not
going to be in the Image
class.
Change is the enemy of perfect design.
A problem with inheritance is that after we aggregate commonalities between different classes into a parent class, if we find an exception to this commonality we need to make big changes.
Composition is an alternative to this. This pattern means reusing code
without inheritance - if there are 2 classes that want to reuse code,
they simply use the code. In the example, this could be done by first
removing the save
and load
methods from the Image
class, meaning it
now represents an image in memory. Now instead of inheritance, we can
pass an instance of the Image
class to access any methods from
it. Now, we can do things like load a jpg image, draw to it, save as a
bmp image.
Inheritance allows reusability by extending code in child classes, and abstraction by using parent classes.
Composition on the other hand allows reusability by using the types
you want, and to allow for methods like save
, load
without caring
about the subclasses via interfaces. Instead of a full parent class
which details what an inheriting object will do (since it inherits all
methods and attributes of the parent class), an interface is meant
to define what an object can do. Interfaces are minimal and can be
easily put onto existing classes compared to parent classes.
Passing interfaces of what you are about to use is called dependency injection.
Composition does create code repetition in interface implementations,
and needing to initialize internal types. We also need to write a lot
of wrapper methods like getSomething
to get data from internal types.
Composition however makes it easy to adapt code to new requirements, and reduces coupling to re-used code (#DOUBT What does coupling to re-used code mean).