Uncle Bob's SOLID principles made easy 🍀 - in Python!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Is no one going to mention him defining (and using) class variables instead of instance variables in the constructor? Is this maybe some kind of `dataclass` confusion? Otherwise really great video.

👍︎︎ 7 👤︎︎ u/MakesYouAngry 📅︎︎ Apr 23 2021 🗫︎ replies

This was really good.

👍︎︎ 5 👤︎︎ u/Kaizen_Kintsgui 📅︎︎ Apr 23 2021 🗫︎ replies

Thanks for this

👍︎︎ 1 👤︎︎ u/infinitejest69 📅︎︎ Apr 23 2021 🗫︎ replies
Captions
the solid design principles help you write great code that's easy to reuse and extend and that's going to save you a lot of time today i'm going to show you exactly how they work using practical examples in python let's dive right into it if you're new here you want to become a better software developer and gain a deeper understanding of programming in general start now by subscribing and hitting the bell so you don't miss anything the solid design principles were first mentioned in a paper written in 2000 by the software engineer robert martin also called uncle bob what's going on with all these weird naming schemes in computer science gang of four uncle bob what what kind of vibe are we going for i'm uncle arjun and have i got something solid for you [Music] solid stands for five principles single responsibility open closed list of substitution interface segregation and dependency inversion i've talked about some of these principles before but in today's video i'm going to take you through a code example of a sales system with order and payment handling in python to illustrate each of these five principles in a practical setting the example i'm going to talk about today is a sales system there's a class order that has items quantities and prices and the payment status and there are functions for adding items computing the total price and paying the order in this example i create an order i add a couple of items then i print the total price and finally i pay the order and this what happens when i run the program so very straightforward we create an order of 210 dollars we're processing a debit payment type and we're verifying that with the security code and the pay method that you see here is basically responsible for dealing with the payments the first of the five principles installed i'm going to talk about is single responsibility we want classes and methods to have a single responsibility another way to say it is that we want classes and methods to have high cohesion be responsible for only a single thing and that ensures that you can reuse them much easier later on in this case the order class does way too many things i mean adding items you can understand it's part of an order because you want to be able to add items to an order computing the total price could also still be part of the order but handling the payment definitely shouldn't be part of the order so the order class has way too many responsibilities and we need to fix that now the way we can do it is for example by extracting this pay method and putting it into a separate class that has another advantage because if later we want to add other payment types like bitcoin or apple pay or whatever we don't have to change the order class anymore we can do it in the payment processing side of things so let's do that so i'm going to create a class here called payment processor and inside that class we want to have the different payment methods now at the moment the pay method is not ideal because there's like an if else statement in here that checks for the various different payment types and i think we want to do something different so let's redesign this method and split it into two methods pay debit and pay credit so i'm going to copy over all this code and then i'm going to refactor it so that it fits with our new design so instead of a pay method we now have a pay credit method and a debit method i'm just copying over everything and then remove what i don't need anymore we no longer need to know the payment type because that's encoded in the method name so i'm removing that here and then i'm going to remove these if statements because we don't need that either so now we have to pay debit and pay credit methods inside a payment processor class the only issue is that we want to set the order status to paid so this means that the payment processor actually needs the order in order to process the payment so we're going to pass that as a parameter now we need to do is not call order.pay anymore but create a payment processor and then use that so now we have a separate pavement processor and we can remove this bay method from the order class so much shorter classes we've made sure that order and both payment processor have their own single responsibility which is handling adding stuff to the order and processing the payment and when we run this code we are going to get exactly the same result as before except that of course i need to add the order object to the payment processor there you go that's the single responsibility principle if you look at what we did we increased the cohesion order has one responsibility payment processor has one responsibility but we also introduced some coupling we'll deal with that later on the second principle the o in solid stands for open closed and that means we want to write code that's open for extension so we should be able to extend the existing code with new functionality but closed for modification we shouldn't need to modify the original code in order to do that let's take a look at what this means in the order example so we have our order we have our payment processor and we have creating the order and processing the payment here in the example now the issue is if we want to add an extra payment method like bitcoin or apple pay or paypal or whatever we have to modify the payment processor class so that violates the open closed principle optimally what we'd like to do is create a structure of classes and subclasses so that we can just define a new subclass for each new payment type in order to do that we need to refactor this payment processor class so let's turn it into an abstract class and create sub-classes for each of the different payment types so i'm going to import the abc module here and let's create an abstract payment processor class that code we're going to need later and we're going to add a single method abstract method that's called pay so now what we can do is for each payment type create a subclass for example let's create a debit payment processor and that's a subclass of payment processor and similarly let's create a credit payment processor we don't need this code anymore and now what we do here is create one of these instances of the subclass and then simply call the pay method there we go let's run this yeah still works fortunately but now we do not violate this open close principle anymore because if we want to add another payment type like paypal for example we don't have to change the payment processor or the order anymore let's try and do that so now i've created a new paypal payment processor and we can use it here without having to change anything about the order class or anything related to that the third principle is lisk of substitution and that means that if you have objects in a program you should be able to replace those objects with instances of their subtypes or subclasses without altering the correctness of the program let's take a look again at this paypal payment processor now the thing is that paypal payments don't work with security code but with email addresses so if i wanted to fix that without changing anything in the code what i probably should do is make sure that this is actually an email address and not a security code so i'd do it like this but then here i would provide some kind of email address the issue is that this is not supposed to be security code but an email address so we're kind of abusing this type to do something different than we're supposed to and that means we're violating the liskov substitution principle one way to solve this is by removing this dependency from the pay method and actually setting it in the initializer so that we can do different things in initializer depending on the type of class we create so let's remove security code here and that means we also have to remove it here here and here and then let's add an initializer and we can add the same initializer to the credit payment processor and the paypal payment processor then gets an email address and we should add self because now it's an attribute of the class instead of a method parameter so there we go now if we create the paypal payment process so we don't pass the email address here anymore but as a parameter to the initializer and now let's run the code again and we get again the same result but now we're properly using the pay method instead of changing what parameters mean in order to fit our specific use case by the way if you're enjoying this content so far give this video a like the fourth principle in solid is interface segregation interface segregation means that overall it's better if you have several specific interfaces as opposed to one general purpose interface i've extended this example to now include a two-factor authentication inside the payment processor class so there's an off sms that gets a code and that authorizes the payment in the debit payment processor i've implemented this in a very simple way just putting a verified variable to true whenever we retrieve an sms code normally you would do all kinds of checks here i'm not implementing that for the sake of simplicity the issue is that a credit payment processor doesn't have two-factor authentication so what we do here is i raise an exception that credit card payments don't support this and paypal payment processor does support it so i have a similar kind of implementation here and the only thing i added to the pay method is checking that the payment has been verified and i'm only doing that in the debit and in the paypal payment process because for credit payments it's not allowed here you see an issue with defining a generic interface like the payment processor to do multiple things that are not always applicable to subclasses in this case not all subclasses support two-factor authentication so it's better to create separate interfaces for this and what you could do for example is add a second subclass of payment processor that adds sms two-factor authentication capabilities so let's add that class here and this is a subclass of payment processor payment processor no longer contains this sms authentication method but we put it in the payment processor sms class and this one is no longer needed because it's already in the superclass and now we can use this class and only inherit from that class if we support sms authentication so that's the debit payment processor and that's the paypal payment processor and then the credit payment processor we don't need to put in this weird sms authentication method that always raises an exception so that's much cleaner this way so that's interface segregation so instead of one general purpose interface we split it so that subclasses can have more meaningful behavior instead of doing this with classes and subclasses you can also use composition which arguably makes more sense in this example what do i mean by that so we have the authentication method here but what we could also do is create a separate class called sms authorizer that handles the authentication let's add that class and that class has two methods one for verifying an authorization code and second for checking that it's authorized for now let's assume that all codes are always valid obviously normally you would add checks here to verify that the code is actually found then we have an is authorized method that returns a bool you could directly access the property but this is a bit cleaner a payment processor still only has a pay method but we don't have this class anymore but now our specific subclasses receive an sms authorizer if they are using that functionality so the debit payment processor doesn't only get a security code it also gets an authorizer and i'm adding a type in here to just make sure that we know what type of object we're getting also now we don't store the verified status anymore inside the payment processor because our authorizer is responsible for that the offs mess function is no longer there and inside the payment method we call the is authorized method from our authorizer and let's do something similar for the paypal payment processor there you go now we need to change the code here to create this authorizer for us and instead of calling off sms on the processor we're calling it on the authorizer and now we should get a similar result as before oh i think i forgot to change this class here let's try that again yes there we go so now what happened is that we used composition instead of more subclasses to create a similar effect myself i tend to use composition more than inheritance because most cases i noticed i don't really need a big inheritance tree i just need to separate the different kinds of behavior in my application and composition works very well for that the final principle in solid is dependency inversion i talked about that one before in another video if you want to check that out click here or here somewhere dependency inversion means i want our classes to depend on abstractions and not on concrete subclasses and in this code this is currently an issue because the payment processes are depending on specific authorizers in this case an sms authorizer so to solve that is create another abstract authorizer class that you pass to the payment processors let's create that class the authorizer class only contains an is authorized method and sms authorizer is now a subclass of that class there we go instead of passing an object of this type to a payment processor we're going to pass an object of this type so let's change that here in the code let's run that now nothing really changed in terms of behavior by doing this but now we can add other things that make this easier to use for example let's say i want to create another authorization method like checking that you're not a robot so let's add another class to deal with that just gonna copy over part of this code but not a robot doesn't have a verify code method it has a not a robot method and now we can do is create another authorizer here and call the not a robot method instead so that's dependency inversion and that works because we made sure that the authorizer is of type authorizer and not of type as a mess off i hope these examples clarify what the solid design principles mean and how they translate to practical applications if you've been using these principles already in your code let me know in the comments below though it is important to know about these principles as a software developer i do notice that as you get more comfortable in applying these they kind of get ingrained into the way you work in my case for example i don't think about these specific principles anymore when i write code i just automatically gravitate towards solutions that incorporate them i have a lot more python videos in the pipeline so make sure to subscribe and come on over to discord to hang out and talk about software design thanks for watching take care and stay tuned for another video of uncle arjun posse of one son why don't you come on over and sit on uncle arjun's lap i love talking in third person [Music] we don't want classes to have low cohesion and be responsible for too many different things first we're going to explain the code example darn and there are a few functions that help me create different orders i hope these examples clarify these these i hope these examples clarify what the solid design principles mean and better better i just automatically go
Info
Channel: ArjanCodes
Views: 46,243
Rating: 4.9748201 out of 5
Keywords: solid principles made easy, solid design principles in python, easy way to understand liskov substitution principle, solid design principles python, solid design principles, solid principles, dependency inversion, liskov substitution principle, interface segregation, single responsibility principle, single responsibility principle python, single responsibility, solid principles of object oriented design, uncle bob solid, Uncle bob solid principles
Id: pTB30aXS77U
Channel Id: undefined
Length: 19min 9sec (1149 seconds)
Published: Fri Apr 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.