8 Tips To Write Clean Code - Refactoring Exercise

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
writing clean code is a skill and you need to practice to get better so I prepared a clean coding exercise for you we are going to look at some code and see how we can refactor it to make it follow clean coding principles what I have here is the order processor which has a single process method accepting an order instance and then it applies a bunch of checks to the order before finally flagging the order as processed this code violates a number of clean coding principles so we're going to fix them one by one and discuss why the refactor code is better the first thing I'm going to tackle is all of the nesting that we have inside of this method so we have one level here level two level three level four so four levels of nesting this increases the complexity of your method because it makes it harder to read and we're going to start by applying the early return principle so what this principle states is as soon as you have some sort of condition that is broken you can safely return from this method so if we were to start applying the early return principle our method would look like this so instead of having a nested order check we're going to say if the order is null then just return from this method and we can take all of this code here which was nested in this first check and move it outside the next condition checks if the order is verified so we're going to negate it and if the order is not verified then we're going to return from this method again we can take this nested load here and replace it with what we have left and the last thing that we can do is also take this part here and again negative so let's say if order items count as equal to zero meaning we have no items on the order then we can just return from this method and let's get rid of this final level of nesting and we are left with this so now instead of the deeply nested structure we have a bunch of preconditions that check if the order is in a proper State and if it is not we just return from the process method and we don't process the order let's still focus on this part here so what we essentially have are guard Clauses in our process method and we can take this a step further by merging them all into one condition so this can be if the order is null or if the order is not verified or if the number of items on the order is equal to zero so if order items count is equal to zero then we want to return so we essentially merged the first three conditions into a single if statement now we can remove these two guard Clauses and we are left with this now the problem is this isn't particularly readable so what can we do about it what we can do to slightly improve readability is replace this check here which checks if there are not any items with the appropriate call to the link any method so we can say if order items not any then we also want to return what we can do to make this better is move this check into either a variable or a method with a descriptive name I prefer to use methods because I find them more readable so we can use the extract method refactoring and let's say is order processable so when is the order processable if the order is not null and the order is verified and we have items on the order working with negative conditions is pretty difficult so this is why I negated all of the Expressions here and here I can say if the order is not processable then my guard Clause fails and I can return from the process method one thing that's slightly annoying is now the process method doesn't have context that the order is no longer null after this check so you could add the null forgiving operator and then you're going to lose the compiler warning now let's talk about exceptions so right here we have two conditions checking some business logic and if the condition isn't met we are throwing a generic exception if you decide to use exceptions as part of your business logic you should always prefer to use specific exceptions so instead of throwing an exception like this we can try to figure out what is the condition that we are checking before throwing the exception so if the order has too many items then we want to throw a specific exception so let's create such an exception I'm going to add a new class and let's call it too many line items exception so we're going to make this public and sealed and I want to inherit the base exception class we're going to copy the exception message that I had in the order processor and pass it to the exception Base Class Constructor so let's create our Constructor and in the base Constructor we're going to pass the exception message and we want to take in the order ID so let's say this is the order ID as the argument of our Constructor and now we can pass it to the base exception message another thing that's going to improve the readability of your code is preferring string interpolation over concatenation so instead of having something like this you can convert it to an interpolated string which is going to be more readable and the benefit of having specific exceptions is that you can include contextual information as properties on your exception so let's say you want to expose the order ID so that whoever is catching this exception can know what is the ID of the order that has too many line items if we go back to the order processor we can replace this with too many line items exception and just pass in the order ID let's do the same thing for the second exception that we have here where we check the order status and if it's not ready to process then we throw some sort of order isn't ready to process exception so let's go to the exception that I already have and use some copy paste magic to come up with the order not ready for processing exception so I'm going to update the Constructor and I want to use the exception message that I have here as part of the order not ready for processing exception and as I said I'm going to be using string interpolation instead of concatenation and I can now move this into its own class I'm going to head back to my order processor and we're going to throw a new order not ready for processing exception and pass it the order ID and for readability let's actually rename this to order has too many line items exception so that we are consistent with the naming convention that we are using in the second exception the process method is starting to look better with the guard Clause that we have here and these specific exceptions that we are using now let's tackle these conditions here which are using some magic values so in this case we have a magic number which is 15 in this case and we don't really know what it's representing and the same here with a magic string representing an order status strings and integers don't really convey much meaning I'm going to make this code cleaner in two different ways so for the magic number we are just going to introduce a constant at the order processor level so let's say we have a private constant integer and we have to come up with a descriptive name let's call it processable number of line items and we're going to give it the value of 15. so now we can replace this magic number here with a constant that has a descriptive name and now this suddenly starts making a lot more sense so if the order has more items than the processable number of line items we throw an exception so now you can see how this starts making a lot more sense and that there's some business concern that we are taking care of for the magic string that we are using to represent the order status I'm going to use something else so whenever you have a status column which can have a few fixed values a really good solution can be using enums so let's create an order status enum and we're going to use it instead of a string so let's say that the default status is pending and let's say we also have a ready to process status with a value of one so I'm going to move the enum into its own file and we're going to update our order class to replace the status with the order status enum and we're going to give it a default value of pending now if I go back to my order processor I can do something like this I can say if the order status is not equal to ready to process then we throw an exception so you can see how using enums can make your code much more readable it's also refactor proof so let's take a look at the entire order processor class so we started with adding a guard Clause adding specific exceptions and then we fixed the use of magic numbers and Magic strings by replacing them with constants and enums one more thing that could be an issue here is that you have different behavior for the guard Clause here and the checks here which are also a sort of guard Clause but are throwing exceptions it would be ideal if they were either all flowing exceptions or that none of them were but if they don't throw exceptions you're losing contextual information on what was actually the problem so what you can do is replace the return type of void with a result object so let's say we add another class which we are going to call Process order result let's convert it into a record and I'm only going to give it two properties so one is going to be the order ID we're going to get it again and an init Setter then we're going to have a string property for our message and again I get an init Setter and let's create a private Constructor which is going to create the process order result and it's going to accept the order ID and the message parameters now let's just assign these to our properties what we're going to do is expose methods that are going to create a process order result instance and these are going to match the conditions that we have in these checks here so let's say we have a public static method returning a process order result instance and let's give it a name of not processable and we're going to accept an order ID and we're going to call our Constructor pass it the order ID and whatever is the message then we want to have for this process order result let's for example say the order with this order ID is not processable and let me just adjust this so like so and let's create the other two results and the only thing that's going to change is the name of this method and the message that we are passing so we have an not ready for processing process order result and we have on has too many line items process order result so I'm going to copy the message from the exceptions that we created earlier so this is the has too many line items and this is the one for the order isn't ready to process so let me just update this here and now if we head back to our order processor we're going to be returning this process order result so let's replace this here and now this becomes process order result not processable and we pass it the order ID I actually forgot that this could be nullable so let's just update this to not have an order ID argument we're going to pass it the default value here and we're just going to say the order is not processable if I head back to the order processor I can just return the process order result not processable now I'm just returning a not processable process order result we're going to then replace this exception here with a has too many line items and we're going to pass it the order ID and we're going to replace this exception here with a new process order result of not ready for processing and we're going to give it the order ID now what happens if all of these actually pass we managed to process the order then we need to return some sort of success result so let's go back to the process order result we'll create a new method that is going to return a new process order result we're going to give it a name of successful and we can also expose the order ID and some success message so let's say we create a new process order result give it the order ID and let's say the order with this ID was successfully processed and now if I head back to the order processor I can say return process order result successful for this order ID so this is what the order processor ends up looking like in the end we started with a deeply nested structure that was breaking a lot of clean coding principles and we ended up with this which I consider to be a lot more readable if you liked this refactoring video take a look at this video next where I'm refactoring from an anemic domain model to a ridge domain model by applying domain driven design principles thanks for watching and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 30,624
Rating: undefined out of 5
Keywords: clean code, clean code book, clean code performance, clean code c#, clean code architecture, clean code horrible performance, clean code principles, clean code design patterns, clean code best practices, clean code exercise, clean code kata, clean code refactoring, clean code patterns, clean code practice, clean code .net, clean code .net 6, clean code .net 7, clean code .net 8, clean code review, clean code how to, clean code habits, clean code refactor
Id: McDvyFglkvU
Channel Id: undefined
Length: 16min 5sec (965 seconds)
Published: Fri Jun 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.