Hello.
In this video, we're going to talk about Question 45, what is the composition of our inheritance?
Principal composition of our inheritance is a design principle stating that we should favor a composition
of our inheritance.
In other words, we should reduce the code by river containing objects within other objects, then inheriting
one from another.
Let's see a practical example.
These grass roots people's data from a database and formats it as a single string.
It's obviously breaking the single responsibility principle, but that's by now not focus on that.
One day the business requirements change, and we are told that sometimes the people information will
be read from the database, but sometimes from an Excel file.
We wanted to be able to make this decision at runtime.
We can solve it in two ways by either using composition and injecting an object, implementing some
people data reader interface, or we can use inheritance.
First, let's solve this with inheritance.
I will make the personal data for motocross abstract.
The rich people mattered must also be abstract so it can be overridden inheritance.
Great.
We achieved what we wanted.
We can now format the personal data sourced from both database and excel files.
We don't have an account applications and we can decide the type at runtime using some factory.
Here is the factory, as you can see, it takes the source in, and based on that, it creates a proper
object.
It returns it as a base type.
All right.
Seems to be working.
Everything looks great, doesn't it?
Well, not so fast.
Let me tell you why using inheritance in this case wasn't our brightest idea.
Now, the personal data for Masterclass is tightly coupled with its inheritance, and the turned in
the base class will affect the child classes.
We can't really use any of those types.
We found that the others provide that.
So if I wanted to use the rich people method and where else I would not be able to without engaging
this entire hierarchy of classes, you can learn more about coupling in the what this coupling lecture.
The relation between those particular classes is reduced.
It is defined at compile time.
Also, we have all the limitations of inheritance, especially the fact that we can only inherit from
a single base class.
This example is simple, but if we need that some other changes that would make those classes different,
we would have the inheritance hierarchy growing really fast.
For example, if the way of formatting the final string would also need to be configurable, we would
need to create even more classes like this.
Such hierarchy would soon become unmanageable if we wanted to create unit tests for those classes.
It would be tricky.
They are two approaches for testing abstract classes and their inheritors, both with their own disadvantages.
We could test only the inheritors and for both of them, would also test the common part belonging to
the base class.
This way, our tests would be partially duplicated.
Alternatively, we can test inheritors ignoring the logic belonging to the base type as much as possible.
Then we can test the base class logic by creating only for the testing purposes.
A special dedicated concrete type derived from it in the tests of this class would focus on testing
only the base class logic.
This is even worse than the first solution.
If we need to create a special inheritors classes for testing purposes only, it means we must our design
really badly.
Another disadvantage of inheritance is that we often inherit more than we actually want.
The base class is exposing the implementation details to inheritance.
There is one more reason for avoiding inheritance that is not really related to this example, but I
want to mention it anyway.
If we use inheritance in the hierarchy of objects that we intend to store in a database using some object
relational mapping tools like Entity Framework, it may be a challenge to store those objects properly.
Databases don't really understand inheritance, so mapping the shop's hierarchy of inheritance into
a flat structure of tables is tricky and often leads to over complicating the model in the database.
Well, that's a lot of issues.
So how can we solve them all?
In this case, we should definitely apply the composition of our inheritance principle.
Let's refactor this code.
First of all, we will introduce an interface.
We will have two classes implementing it.
Instead of using inheritance, we compose the personal data for formatter with any type implementing
the open data reader interface.
Finally, we will adjust the factory.
Let's make sure everything works.
Great.
Everything works as before, but no problems that we mentioned.
OK, you now the glasses are now loosely coupled.
They live in complete separation and they only communicate over an interface.
The relationship between glasses is not rigid anymore.
It is defined at one time when we actually inject a concrete people.
They trigger the personal data for motor object, which deconstruct constructor before the relation
was defined at compile time.
If we needed to add more changes, the inheritance hierarchy wouldn't grow.
We would only add a new interface and classes implementing it.
For example, some oh person formatter interface implemented by person showed data for motor and person
full data for motor testing would be simple.
We would test each class in separation and no tests would be duplicated when testing this class would
simply inject a mark of a pupil data reader interface.
Lastly, no class exposes and implementation details to uncover class.
All right.
I hope you see now that in this case, composition of our inheritance was a rule who are following.
I would say that in the majority of cases, you should follow their composition, design rather than
inheritance.
To be honest, at my everyday work, I almost never use inheritance.
One more thing before we move on.
If you know the breed design pattern, this all may sound very familiar to you.
This is because the bright pattern is simply a way of implementing the composition of our inheritance
principle.
All right.
We said that having a composition instead of inheritance has a lot of benefits, but it doesn't mean
that inheritance should be avoided at any cost.
Let's take a look at the person type.
Now, let's say we wanted to introduce an employee type to the product.
An employee is still a person and it should have first name, last name and year of birth properties.
Additionally, this type should have a position in property.
Let's say we are so excited about using the composition of our inheritance that we decide not to use
inheritance ever again and that this is the code we create.
Is this design good?
Well, I wouldn't say so what looks a bit fishy are the forwarding methods.
So the methods that only exist to call methods from some inner object.
In our case, those methods are the first name, last name and year of birth properties.
Remember that properties are like special kinds of methods.
Now let's see how this court would look like if we used inheritance.
Well, I think it looks much simpler.
The forwarding methods are not there as the properties we want to have in the employee class are simply
inherited from the person class or we need to define is the new position property that actually makes
the employee different from a person, how to decide whether to use composition of our inheritance.
Well, first of all, you need to answer this question when thinking about your types.
Can you say that one of them is the other one?
Do they have the same structure and similar functionality with only some extended behavior in the draft
type?
If so, inheritance can be the right choice.
Otherwise, you should rather opt for composition.
When in doubt, go for composition, and in the worst case, you will adjust your design if it turns
out it's not working out in the document attached to the structure.
I linked an interesting article about making the decision whether to choose composition or inheritance.
Let's summarize Composition of our inheritance is a design principle stating that we should favor a
composition of our inheritance.
It means that we should reduce the coat by containing objects within other objects rather than inheriting
from one another.
When discussing this topic during the interview, you can be asked What is the problem with using composition
only if we decide not to use inheritance at all?
We make it harder for ourselves to define types that are indeed in an ISA, a relation.
So correlation when one type is the other one.
For example, a dog is an animal or an employee is a person.
When implementing such a hierarchy with the composition, we create very similar types that wrap over
types, only adding a bit of new functionality, and they mostly contain forwarding methods.
So what are forwarding methods?
There are methods that don't do anything else.
Then calling almost identical methods from some other type for working methods indicate a very close
relationship between types, which may mean that perhaps one type should be inherited from another.
All right.
We learned a very important principle.
Thank you for your attention, and I'll see you in the next lecture.