Why COMPOSITION is better than INHERITANCE - detailed Python example

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I have mixed feelings about this. It's advice you'll hear repeated often, and is often good advice for many isolated projects, but I think the video only scratches the surface of design aspects when it comes to composition vs inheritance.

The topic gets particularly more complex when you think about how code sharing works in a real ecosystem where you may be consuming code from upstream that you have no control of and/or you may be designing code intended to be used by other projects downstream.

I don't think it's useful to generically flatten the argument to say "always favor composition". You have to look at the use case and figure out which might work better based on the project details and the goals you have in mind, particularly when it comes to code reuse.

I personally find that, when writing code that is going to be consumed by others, designing for inheritance (which does not necessarily require using it extensively) ends up making many projects most flexible for downstream consumers. How you write you compose your class and its methods can greatly impact what downstream users are able to easily change for their own unique use-cases that you'll never be able to cover in your own codebase.

As Raymond Hettinger once put it shortly:

Those who completely eschew inheritance and only use composition will eventually find themselves reinventing what inheritance already does (and will do it badly, slowly, and unconventionally).
It starts with "I want to automatically delegate this method call to a method of one of my attributes".

Then it escalates to "I want to check a list of objects to find the first one that has this method".

And it culminated with "I want to reuse that same logic elsewhere".

👍︎︎ 10 👤︎︎ u/ManyInterests 📅︎︎ Jun 11 2021 🗫︎ replies
Captions
many of the design patterns in the gang of four design patterns book are based on the principle favor composition over inheritance but what does that mean let's find out if you're new area you want to become a better software developer gain a deeper understanding of programming in general start now by subscribing and hitting the bell so you don't miss anything if you want to separate responsibilities create code with higher cohesion there's a couple of ways to do it one way to do it is inheritance so instead of putting everything in one single big class you would create a class hierarchy of classes and subclasses where you would put certain things in a subclass so that it would be separated from the main class another thing you can do is composition that means that you're basically using separate classes to represent separate things in the application and then each of these classes use each other in some meaningful way it's basically the difference between the is a relationship which is inheritance and he has a relationship which is composition even though both inheritance and composition allow you to separate responsibilities you have to really watch out with inheritance because that actually introduces the strongest possible coupling in object oriented programming and i'm going to show you why in an example so this example is a very advanced employee management system we have an hourly employee who's paid based on the number of worked hours we have a salaried employee that gets a fixed monthly salary and we have a freelancer now freelancer is not actually an employee but if we consider employee as a person that gets paid by a company for work performed then it kind of fits under that same umbrella in the early employee we have some personnel data like the name and some id we have a part that's about commission so if an employee lands the number of contract the employee gets a commission we have pay rate number of hours worked and there is a kind of fixed employer cost and then we have a compute pay method that actually computes how much the employee should be paid based on these values here so to compute this is relatively straightforward we multiply the pay rate with the number of hours that the employee worked we add the fixed employer cost and we add the commission for each contract that the employee landed seller the employee is quite similar it also has a name id it also has a commission structure but there's a monthly salary and the percentage of time that the employee works and then this is the compute pay method and then finally we have freelancer also name and id commission pay rate hours worked and then we have an additional that number for taxes and then we have a main function where we create a couple of these employees print out some information and this is what happens when i run this example so both of these employees earned six thousand dollars now let's analyze this code a bit there's two main problems the first problem is that there's a lot of code duplication i mean early employee has commissions contract landed so has salaried employee freelancer has this as well there's a lot of duplication in the way that the pay is computed so there's probably a better way to structure this than having these three separate classes that have a lot of the same code another issue is that each of these classes have a lot of responsibilities for example here this one is responsible for storing personnel data it's responsible for dealing with commissions and it's responsible for the pay rate and hours worked and how to compute the pay based on that and the same goes for the salaried employee and for the freelancer as well so two problems that we'd like to solve and you can use inheritance to do it by basically creating a hierarchy of classes and subclasses or you could also use composition and that's basically separating out the different aspects of what a class consists of and then combining them later on i'm going to show you both of these techniques and also show you what the difference is in terms of coupling and how that plays along with the principle of favor composition over inheritance so let's first look at the inheritance case so what we're going to do here is basically create a super class for each of these employee types that store some generic data that's useful for every employee and let's call that class employee i'm going to turn that into an abstract base class and let's also use data classes here so we have a data class employee that is going to contain the name and id of the particular employee so i'm just going to copy this over here and this is going to have an abstract method compute pay and let's add some dog strings while we're at it so now we have this basic employee class and then obviously only employee and salary employee and freelancer are going to inherit from that and then in early employee we don't need this part anymore and same for salaried employee and for the freelancer there we go let's verify that this still works correctly and it does so now i've done we've used inheritance to separate out a bit of information about employees and separated that from the rest so for this inheritance works fine but the problem is each of these classes still have too many responsibilities for example there is both commission information here and pay information so it would be nice to also separate that out if you want to use inheritance what you need to do is create sub classes for each of these employee types to have a version with commission and version without commission so for example what you could do is create a class salaried employee with commission that is a subclass of salaried employee so let's do that i'm just gonna copy this entire class and then i'll remove what i don't need so we have the salaried employee with commission and that's a subclass of salaried employee that's simply paid based on a fixed monthly salary and that gets a commission so i can remove the commission part from the original salary employee there we go and then what i do in the salary employee with commission that i just put in the commission stuff i remove this because that's handled by the salaried employee and in compute pay i return calling the compute pay method on the superclass and then adding the commission and now what i need to do here in this main function is to make sure that sarah because sarah is actually an employee with commission is now no longer a salaried employee but is a salary down please with commission there we go and let's run this code again we're gonna get the same result but now you see we used inheritance to separate these different responsibilities a bit more if you want to do this for other employee types because obviously the freelancer also has that same commission structure and the auli employee also has the same commission structure i would have to create a hourly employee with commission that's an hourly employee subclass and a freelancer with commission that's a freelancer subclass so i'll do it one more time for the freelancer just to show you what what is going to happen so now we have a freelancer with commission and that's basically a subclass of freelancer and that gets a commission this stuff i'm going to remove and just like in the salary employee with commission i'm using the super class compute pay method here so that's basically this remove that extra plus and that's basically what we need to do and now i can remove the commission stuff again from this part and remove it here as well so now i've basically done the same thing but for the freelancer you already see where this is leading to issues because one thing we didn't really solve the code duplication this way because if you look at freelancer with commission so that's what this looks like and you look at salary complete with commission these classes are more or less the same basically the only difference is that they're a subclass of another class this one is a subclass of cellular employee this one is a subclass of freelancer so code duplication we didn't really solve another issue by doing this with inheritance is that basically for every variation that we're going to add we're going to get this explosion of subclasses for example suppose you also want to have a yearly bonus that's included with the pay then you would get lots and lots of subclasses like freelancer with commission without bonus a salaried employee without commission with bonus etc etc so in the end that kind of approach doesn't really work and that's also the crux of what it means when we say favor composition over inheritance if you use inheritance too much to separate the responsibilities in this way it means you're going to end up with this huge hierarchy of subclasses and it's going to be really difficult to deal with also because inheritance actually introduces a lot of coupling because for example here the freelancer with commission uses again the compute pay from the super method so it assumes things about what compute pay does in the super class so that's what happens when you do this with inheritance now let's look at another option which is composition and with composition we're not creating hierarchies of classes we're trying to separate out the concepts and then combine them in meaningful ways in this case we have a few different concepts we have the employee with the employee data we have the employee payment structure which is either hourly or salaried or on freelancer basis and we have the commission which is based on the number of contracts that an employee has landed so what you can do instead of using inheritance to combine all these things you could also create separate class hierarchies for each of these three different things and then combine them later on what you could do in this example is create a commission class and a contract class and then combine them with the employee class later on so i'm going to leave this generic employee class here but i'm going to add these contracts and commission classes and then you'll see how that works together so first thing i'm going to do is create a commission class and then i'm just going to copy over this part because it's going to be very similar to this but a bit different so we're going to have a class commission and that stands on its own and that class is going to have a commission amount and the number of contracts landed and it's also going to have a let's call this getpayment method that computes the commission to be paid out there we go and this we can remove because we don't need that any longer so that's the commission class really basic now let's also add a contract class for the contract i'm going to use an abstract base class because then we can make subclasses like hourly contract salary contract freelancer contract and the only thing that the contract class is going to have is a get payment method so let me just copy that over from this class that's an abstract method and let's add docstring there we go so now we have both a commission we have a contract class and we have the employee class and now this is where composition gets into play because what we can do now is basically assign these contracts and commissions to an employee so an employee is not only going to have a name and an id but it's also going to have a contract and an employee is also going to have a commission now because not every employee is going to have a commission we're going to turn this into an optional type so i need to import that from the typing package and default that's none and now employee actually doesn't need to be an abstract base class anymore it can be just a regular class because that's we're going to combine everything into one and then the compute pay method we simply use the methods from the contract and from the commission to compute what the pay should be so first we're going to compute the regular payout from contract and then if there is a commission we're going to add that to the payout and then as a last line we're going to return the payout so what we've done now is define the relationship here between the employee the contract and the commission that's basically what's happening here and then we deal with how that interacts with each other in the compute pay method and now what we can do is create specific contracts so i could create an hourly contract so i'm just going to use the hourly employee class for that and change it a bit so the hourly contract that's going to be subclass of contract obviously and then that's a contract type for an employee that's paid based on the number of worked hours and that contract type what what does it have well it has the pay rate and the hours worked and the fixed employer costs and let me just replace this method with the get payment method there we go and then the only thing that this get payment method does is return the pay rate times the hours work plus the employer cost and the commission is handled somewhere else this can actually be removed because that's already defined in the super class so now we have an hourly contract and i can make a salary contract as well and that basically looks like this and then finally let's also create a freelancer contract just for completeness i should have copied that from the hourly contract there we go and there should be a fat number so that should be it i can remove all these complicated with commission classes because we don't need them anymore let's just remove everything here now there's a couple of things we need to change in the main function because now we obviously change the whole structure of the application so we have commissions contracts and employees and in the main function we need to basically construct these things so for the first employee let's create a contract and that's an hourly contract with pay rate of 50 and 100 hours worked and then henry becomes a regular employee with a name and an id and with a contract for the second employee we're going to do something similar so there is a sarah contract and that's a salaried contract with a monthly salary of 5 000 and then sarah is just like henry a regular employee instance and we provide it with the contract second thing that we add to sarah is the commission so let's create a commission object with 10 contracts landed and also pass it to the constructor there we go now we only need to change a few things in information that we print because the hours worked is actually part of the contract there we go and the contracts landed is actually part of the commission and there's one thing i forgot to do which is in the employee class we remove this abstract method because compute pay is no longer abstract obviously so let's save this and now yeah that's fixed and let me run this example one more time and check that it actually still works and it does so what are the advantage of using composition in this way versus inheritance well one thing is that we've avoided a lot of the code duplication that we had with inheritance because basically everything that's related to commission is now in a commission class and everything related to contract is now in a hierarchy of contract classes in the employee class we're combining everything so we have the personal data we have to contract and we have an optional commission and that means when we create the employees we have a lot of flexibility in determining what kind of employees they are i could create a employee with an hourly contract with a commission or a salaried contract without a commission or the other way around so there's a lot of options here and we don't have that combinatorial explosion of classes and subclasses anymore that we had with the inheritance case another thing that's nice is that this is actually pretty easy to extend with other types of contracts or other types of commissions at the moment there is still a single commission class but we could make this a little bit more generic by adding an actual abstract commission class let's let's do that quickly so let's call this a contract commission and then let's create an abstract class called commission and the only thing that class has is a abstract method called get payments and then basically i'm going to copy over the docstring here there we go and contract commission is then going to be a commission subclass and then what we can do in the main method is actually say that sarah gets a contract commission that gets a number of contracts that she landed and when we run this we're going to get exactly the same result obviously but we made this a little bit more generic because now employee has a contract which is an abstract class and has a commission which is also an abstract class so we can now make all kinds of different subclasses of contracts and commissions and combine them in any way that we like and that's the power of composition to be able to do that there's a few limitations to the example that i just showed one thing is that the employee class still has a lot of responsibilities it has both personnel information it has a contract and the commission we kind of separate those out but it does bring all of these things together so it might make sense to actually separate this out as well into a person data or employee personnel data class that basically represents that part of the employee and that would also allow you to create other kinds of subclasses like if you have specific types of information that you would store for different kinds of employees then you would also be able to do that second thing is that i use data classes here all over the place i mean i do like data classes i think they're really simple and they allow us to easily create classes containing data such as a specific commission type or a specific employee type basically like i did here you don't have to use data class here you could also do it with regular classes then you have to add initializer methods so i think this is a bit simpler but there are also other options if you're not too fixated on using built-in stuff only you could also use a library like pydentic which gives you a lot of extra options with regards to data classes for example you can imagine that employee data is something that you read from a file and pydentic offers validation and sanitation options so you get a bit more control over what happens with that data in my own experience i use inheritance almost exclusively with abstract base classes so it's mainly a mechanism to help me separate out different parts of the application and then patch them up at the end if you look at most of the design patterns in the gang of four book they actually work in the same way it's rarely a design pattern which has more than a single layer of inheritance the decorator is an example of a design pattern that has two layers because it actually has a recursive component in the definition of the pattern but most of the other patterns are really just a single layer of inheritance so overall use abstract base classes to define the interfaces and then define subclasses for the specific versions of that and patch them up at the end another way to look at it is in terms of coupling so what you do with an abstract based class is that you're reducing coupling between various parts of your application because you're allowing one class to depend on the interface instead of a direct instance and that's why abstract base classes help you reduce coupling in your application inheritance in general if you're not using abstract based classes is actually a mechanism that adds coupling because every time you create a subclass that subclass is directly dependent on the super class so if your aim is to reduce coupling in your application then realize that inheritance actually takes you in the other direction whereas abstract based classes do help with reducing coupling and that's why composition is such a great mechanism for writing nice decoupled code what i also used in this example but didn't really talk about is dependency injection and dependency inversion i did a separate video about that there's a link in the top i hope you enjoyed this example and that you thought it was useful i put the code that i worked on in the git repository you can find it in the description thanks for watching take care and see you in the next video [Music] you
Info
Channel: ArjanCodes
Views: 53,137
Rating: 4.9554086 out of 5
Keywords: composition vs inheritance, composition vs inheritence, composition vs inheritance python, favor composition over inheritance, favor composition, python software design, software design principles, software design, design patterns, inheritance or composition, software design principles solid, software design principles loose coupling, software design principles and concepts, software design principles cohesion, software design principles in software engineering, inheritance
Id: 0mcP8ZpUR38
Channel Id: undefined
Length: 23min 29sec (1409 seconds)
Published: Fri Jun 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.