Beginner Programmer's Guide to Composition in OOP (Examples in C# dotnet!)
March 20, 2023
• 6,175 views
Are you a beginner programmer learning about Object Oriented Programming (OOP)? If so, you've likely heard of Inheritance, but what about Composition? Check out this video to understand the basics of Composition with some code examples in C#!
Want the code used in this video? Check it out on GitHub:
https://github.com/ncosentino/DevLeader/tree/master/VehicleCompositionExample
For more beginner programming videos, check this out:
https://www.youtube.com/watch?v=_v9k74gWJtE&list=PLzATctVhnsgh_G9...
View Transcript
unfortunately for inheritance I use the better example but when we're talking about composition I still think that we can come up with something that's useful to demonstrate how it works we're going to be diving into object oriented programming in particular we're going to look at composition now many of us when we're starting out on our software engineering Journeys we are taught inheritance and composition often comes way later so the purpose of this video if you're starting out as a junior software engineer is to get you introduced to composition nice and early now if you're a junior software engineer then I highly recommend you watch to the end of this video so you can learn about another skill that you should learn early in your software engineering journey and right before we dive into it we'd love to hear from you in the comments how many
of you as a junior software engineer learn about composition and inheritance around the same time I'll be curious what people have to say alright let's get some diagrams going so for many of us when we're learning about object oriented programming early on in our software engineering Journeys we're taught about inheritance and inheritance shows you a relationship between objects where you as the name suggests inherit from things and are able to get the functionality and properties from the ancestors so in this quick example that I have on the screen here you can see that animal is is the ancestor and then we have things that inherit such as mammals inherent from animals and then dogs inherit from mammals and these things are able to inherit the properties and functionality of their ancestors but when we talk about composition we're going to turn this on its head
a little bit so let's talk about some of the key points of composition so when we talk about composition the first thing that I want to call out is that we're going to be taking smaller more purpose-built objects and then creating more complex objects out of those so as the name suggests we are composing more complex objects from the smaller more simple ones a key advantage to this if you're not familiar with it is something that's called the single responsibility principle so when we have smaller objects that we're working with they're generally more simple than these complicated inheritance hierarchies that we could end up creating and because we have more simple objects we're able to stick to the single responsibility principle more easily and the single responsible ability principle states that we have functions in classes that are really focused around one purpose or one
responsibility and the idea here is that the more things that we end up adding and the more responsibilities that we give to a particular object the more complicated it is to work with maintain and test in object-oriented programming when we use inheritance we're actually able to override our parent or ancestor functionality or properties now when we're talking about composition we can still achieve this type of functionality but what we're not going to be doing is overriding properties and methods in an ancestor and instead we're actually able to change out implementations of the different pieces that our object is composed of so we'll demonstrate this but I just wanted to call out that we're able to achieve a similar type of behavior change even though we're not going to be overriding things and using this inheritance model and while there's going to be other benefits of
using composition the last thing that I want to call out is that it will generally lead to code that's easier to test Main contain and extend and of course people can call out individual circumstances where this argument might not hold but this is why I'm saying in general I think that using composition you're able to get these properties more easily than using inheritance in fact I would argue most of the time inheritance when you get into complex hierarchies actually makes these types of properties more difficult to work with so quickly before continuing here's some of the key points that I've already called out and I just wanted to add that in general I found that a lot of us start off with inheritance in the beginning when we're learning about object-oriented programming but I do really think that composition is a much more useful way
to design software and the unfortunate thing is that many of us including myself kind of get trapped in this inheritance style of programming and sometimes it can take us a really long time to get out of this pattern because this is what we were taught early on and it's kind of ingrained in our behaviors so with these key points out of the way let's actually get to diagramming some things that we could use composition for so the example that I'd like to use for composition is Vehicles because I think that this will help demonstrate how it works because there's more tangible pieces that we can refer to remember when we're talking about composition we're starting with smaller pieces and then building more complex objects out of those smaller pieces so like with the vehicle these are composed of more simple parts and when you aggregate
them all together you have something really complex that runs and functions so what we're not going to be doing is using this type of model like we have for inheritance where we would have types of vehicles that would inherit from the vehicle's ancestor so we would not be creating classes like motorcycle car and truck that would inherit from a vehicle class this is inheritance that's not what we're going to be doing here but I think if we write out some examples like this then we can ask ourselves some interesting questions so what types of parts do these things have if you think about a motorcycle a car and a Truck literally what types of parts do they have for example each of these would have an engine each of these would have wheels and tires now if you think about cars and trucks these two
things have doors but most normal motorcycles do not have doors on them and even if you're looking at cars and trucks that have doors do all of them have doors do all of them have the same number of doors another question we can ask is what type of functionality does each of these parts have so for example each of these three things that I've drawn on here has an engine what's the engine responsible for how about the wheels on the tires what are they responsible for the doors on cars and trucks what kinds of actions can those things perform personally I think it's helpful to think about composition this way because we can take real life objects and think about how they are built up from smaller individual objects a lot of the time in software we're not dealing with really tangible things like vehicles
in this example but you can apply this type of thinking for the classes You're Building to think about the individual pieces that you want your class to be made of so let's pull these over to the side and talk about the different things that might make up each of these vehicles we could have something like a chassis and we already discussed engine Wheels tires and doors of course in real life These are composed of way more things but for this example we don't have to go Define all of them even the engine or the motor in this example would be incredibly complex if we were to look at it from a composition perspective so completely inverted from our inheritance diagram when we're talking about composition this is how I would like you to think about it we're going to take each of the individual components
that make up something like a vehicle and pass in implementations of those things into a vehicle class again that means that we're not inheriting from vehicle and creating a motorcycle a truck or a car but instead we're going to be passing in different implementations of these different things that I've captured in this square box into the vehicle class so if we were to create a vehicle that was supposed to be a motorcycle what types of implementations of these different components would we be passing in to make a vehicle well we said earlier that a motorcycle does not have doors so we would pass in for our list of doors there would be zero Pro or we could have a door implementation that was something like no doors so if we're considering the wheels for a motorcycle we would be looking at different properties especially compared
to a truck or a car the first thing that we would be calling out is that there's only two wheels for a motorcycle for each of these wheels we might have properties such as the diameter and the width of the wheels and if you were to contrast those with the properties of the wheels for a truck and a car these wheels would likely be much smaller both in diameter and width compared to a truck in a car now a very similar example would be the tires right because if there's only two wheels for a motorcycle there's only going to be two tires and of course if the wheels are much smaller then the tires are likely going to be much smaller as well when we compare them with a truck and a car how about the engine for a motorcycle the implementation of an engine
for a motorcycle would look dramatically different from a truck and a car for example if the engine had properties on it such as the number of cylinders or the display basement of the engine a truck might arguably have the largest displacement and the most cylinders and maybe a car would be second to that whereas a motorcycle would have far less displacement and fewer cylinders compared to both now for those of you with really crazy motorcycles yes I realize this might not always be true but this is just an example and realistically this is probably going to hold true if we consider the chassis for something like a motorcycle of course this is going to look dramatically different from a truck in a car perhaps our chassis would have properties such as the material that it's composed of the weight of the chassis and the chassis
might even Define some Dimensions that it has or points where we could Mount these other components to so you've done a great job with putting up with my terrible diagramming so far so I figure you probably want to see a little bit of code even if you're not totally familiar with the language I'm going to use it's probably helpful to see some of these Concepts taken from diagrams and real world examples and translated into something like code just so you can see what it's going to to look like so I'll show you that in C sharp alright so I'm here in visual studio and I have a simple Vehicle Record that I've created and I'm just going to be using a record here to keep things simple and then I can print things to the console and you can see how it all works I've
omitted a couple of the components we talked about just to keep this more straightforward and I've introduced an engine with an i engine interface doors as an array of idor interface and then Wheels as an array of iwheel interface if you're not familiar with what an interface is this is just going to define the functionality that we expect to have for each of these three different things so let's go ahead and start with the idor interface you can see that we've defined functionality to check whether or not the door is open or if it's locked that's going to be these two properties here and then I've also made methods so that we can open close lock and unlock the door here just to keep things super simple the i engine interface will have a number of cylinders and then a displacement on it as well
I've made it a string just so that we can print things out a little bit easier and have them be more visible and finally the eye wheel interface is just going to have a width and diameter on it so now that we've looked at that how do we go Make an instance of a vehicle that would resemble a motorcycle versus a truck so aside from our vehicle everything that we've looked at so far is an interface and that's going to mean that we need other records or classes to implement these interfaces so that we can go construct a vehicle if we start with something like the engine I just wanted to show you two different ways that you could go about this we could have something like a generic engine that takes in Arguments for the number of cylinders and the displacement so that we
don't need to go make a unique implementation of this type this would mean that for a motorcycle I could go make a generic engine instance I could pass in it that it has two cylinders and a small displacement on the other hand what you could do is you could also make a very specific implementation that has our I engine interface and then has very explicit properties defined for it now the example that I'm using is a little bit contrived just because we're going to be dealing with some really simple properties to print to the screen but there's going to be situations in your code where you may in fact want to have very specific implementations like this especially when it comes to functions and methods and there may be other situations where you want something more like this just so that you can have a
really easy way to create these instances now when it comes time to actually create an instance of a motorcycle we would just be creating a new instance of a vehicle and then passing in the different objects that we want to make up that vehicle in our example we could go make a generic engine pass in two for the number of cylinders and then we could say that we have a really big powerful motorcycle that has a 1000 cc motor but what about the doors and the wheels well the doors are kind of easy because we did say that motorcycles don't actually have doors so we can pass in an empty array of side door and not actually have to have an implementation of any doors at all and because we didn't actually have anything made for Wheels yet we could follow the same pattern that
we use for the engine and create a generic Wheel record and then because it's an eye wheel we need to have a diameter and a width as well now because I'm just kind of rushing this example I didn't actually have units and things associated with these so we can just kind of make up some numbers when we go to create this if we go back to instantiating the motorcycle we could create a new array of eye wheel and then pass in two instances of Wheels this is because motorcycles of course have two wheels now for our motorcycle we could actually pass in two very different Wheels in this example we could have the first wheel Let's Pretend This is the front wheel it could be 16 inches in diameter and have a width of six Let's Pretend This is six inches and to be honest
I actually don't know how wide motorcycle wheels are if we had this second wheel to be the back wheel perhaps it's actually a little bit it's smaller in diameter we don't have the tires here but maybe it's a thicker tire and the wheel itself is a little smaller and maybe it's three inches wider so if you imagine this motorcycle it's got a bigger wider wheel in the back and actually now that we have all of these created we actually have an instance of a motorcycle let's go ahead and print our motorcycle to the console to see what values it has now pardon the crappy formatting here but we can see that in the console we have our Vehicle Record the engine is of type generic engine and you can see that we have two cylinders and we have a 1000 cc displacement as well the
doors we actually don't have any doors so that's totally expected and wheels actually didn't end up printing out the wheels that we wanted to see here now this is just because the string formatting for the record type so let's cheat a little bit and print out the first wheel we have and the last wheel we have in our array and there we go so of course this part did not change from when we previously looked at it but you can see that I'm printing out the first wheel and the second wheel so coming back to composition you can see that we actually made a vehicle that has nothing to do with the type motorcycle car or truck but instead the individual pieces that we passed in when creating our vehicle actually make up the things that make a motorcycle super quickly let's repeat this for
a truck now we're going to make a truck we can actually go if we wanted to swap to using Nick's unique truck engine that I made below we can still use the generic one if we wanted to and just make it have a larger displacement more cylinders but let's try it out with this implementation we're going to have to come back to doors because we never actually made these and for the wheels instead of just having two we can make four that are significantly larger let's go look at what we can do for idor so we can get a little bit more creative with this implementation of a door compared to some of the other interfaces because we actually have some methods to work with and some State now a truck is going to have four doors and had the methods close lock open and
unlock as well and you'll notice that each of these is pretty simple except open actually has some interesting Behavior because we can have a locked and unlocked State we want to make sure that if we try to open the door when it's locked that it should not work of course I caught a bug so let's put a return statement here so that we don't actually open the door after we say that we can't open the door so now that we have generic door let's go add four of them for our truck if we go ahead and run this we can see that we still have vehicle information being printed to the console but it's a very different type of vehicle now our engine is Nick's unique truck engine and we can see that it has eight cylinders and a displacement of 10 liters this is
way bigger than the motorcycle now doors and wheels did not print again because this is due to how the record type prints out with arrays but we can actually jump back to the code and see if we can play with the doors and see how they work using our truck let's add some console right lines to see the state of our our door for our truck and we'll assume that the first door in our array and we'll assume that the first door in our array is the driver's door in the front so to start the driver door is not open and the driver door is not locked what we can try to do in code is actually open up that driver's side door and then see if it actually opened up afterwards and if we look at the output here we can still see that
it starts off as false and false and then after calling open on the door we are able to see that the state of it is now set to open as true here let's go back to the code and actually try to lock the door before we open it and see what happens when we run this program now you can see in the console that we actually see this line the door is locked and afterwards the door is not open and the locked state is set to true so in fact because we locked the door we weren't able to open it now because this is a talk on composition if you wanted to change the behavior of how this door worked you don't actually have to go change anything about the vehicle class at all instead you could go Implement your own implementation of idor and
handle these things completely different perhaps if the door was locked and you tried to open it you wanted to raise an alarm what would that look like well just to make this super quick we could go make a whole new implementation called alarm door that would largely be the same as what we had before but instead we could go make the console printing just print out something a little bit different and if we look at our truck implementation if we wanted to have alarm doors only in the front of the car then we could go ahead and change these first ones just to be alarm doors now I'm just going to be a little bit lazy here and assume that the last door in the array is the passenger side rear door and like we've been doing so far the first door in the array
is the driver's side front door so if we try to lock it then open it and do the same thing in the rear what would happen well the first line we get in the console is loud alarm blaring and that's because we have an alarm door that we were able to compose our vehicle with as the first two doors in the array the second two doors are just the standard generic doors and they just print out that the door is locked when we try to open them and they're locked so hopefully you found this explanation of composition useful we started by talking about some key points for composition over inheritance and then started navigating some layouts to see how composition actually looks compared to inheritance we use the example of vehicles because I think Vehicles work really well when we can think about the different
parts that go into each type of vehicle that we want to talk about from there we looked at a code example where we could actually change the implementation and functionality of the different parts in our vehicle to make different vehicles and the cool part about this is we never had to go make a dedicated motorcycle class a dedicated truck class we never had to go override properties instead we could go make purpose-built classes and go make a more complex object out of these classes and because he watched right to the end I did mention early on that if you're a more Junior software engineer than I have a skill that you should focus on early in your software engineering Journey so now that you understand composition you can go learn about this other skill over here
Frequently Asked Questions
What is the main difference between composition and inheritance in object-oriented programming?
The main difference is that composition involves creating complex objects from smaller, purpose-built objects, while inheritance creates a hierarchy where a child class inherits properties and methods from a parent class. With composition, we can change implementations without altering the parent class, leading to more flexible and maintainable code.
Why is it important to learn about composition early in a software engineering journey?
Learning about composition early is important because it encourages better design practices, such as adhering to the single responsibility principle. This helps in creating simpler, more maintainable code and avoids the complications that can arise from deep inheritance hierarchies.
Can you provide an example of how composition is used in a vehicle class?
Sure! In a vehicle class, instead of creating separate classes for motorcycles, cars, and trucks that inherit from a vehicle class, we would compose the vehicle using smaller components like engines, wheels, and doors. Each vehicle can then be created by passing in specific implementations of these components, allowing for greater flexibility without the need for complex inheritance.
These FAQs were generated by AI from the video transcript.