Make Your Code Clean With the SOLID Principles

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome back to new video in this video i will talk about the solid principles specifically for android developers if you don't know what the solid principles are those are basically five different principles that make your code clean so for each letter of solid there is one principle and if you've never really dealt with them they are quite hard to understand especially if you google these then there are some really really hard to understand explanations you don't really know how you can apply them in practice so what i want to try in this video is i want to give you really practical explanations here in an android project um most of these won't be very android specific so even if you're not an android developer you will understand this but i try my best to put that in the in the best practical perspective here possible and before i will start here i want you to take a look at this main repository here and check if you find that there is something wrong with this repository and this login user function so you can see we just get our firebase authentication instance here in the constructor and with that we then sign in to firebase and if there is an exception we will simply log the error message in our error log text file so just pause this video for a minute take a look at that code and check if you find that there's something wrong in this code you can also write that into this video's comments right now so what you think what is wrong here i will now give you the explanation what is wrong and that is this function violates the the first principle of the solid principles which is in the end the s so that s stands for single responsibility and the responsibility here is in the end a reason to change so so let's take a look here at this login user function if we imagine we want to change the login user function why could that be why could we possibly want to change this so we could change it because we want to change something with the way we authenticate the user here that would be possible but we could also change it because we would want to have a different way of logging here so there are two different reasons why we could possibly change this function and that valids the the single responsibility principle so whatever class you have or whatever function you have these functions and classes should always only have one single responsibility and one reason to change so let's now take a look how we can fix this in particular well what we can do is we can create a separate class that represents such an error logger here so let's create a new class and call that file logger and this file logo function will then just have a function to log the error for example something like this and we simply cut this out here paste it in here we replace this with our error and now we can use such a file logger in our repository we could pass that here in the constructor file logger and then if we catch an exception we use our file logger and simply log an error which is represented by our error message here and now suddenly this function only has one possible reason to change which is this authentication here so if we want to authenticate the user in a different way but if we want to change the the way we log the message we don't change the login user function instead we change the function here in our file logger so now this class has that single responsibility of simply logging something the next principle is the o which stands for the open closed principle so what that basically states is that objects or entities should be open for extension but closed for modification so that is what you will read all over the internet and that is confusing i know so let's declutter this and explain it in a more simple way so it says classes should be open for extension so we should be able to extend these classes but close for modification so we shouldn't be able to change the way a class works what this means specifically is let's take a look at this file logger class now if we would change the way we log error messages here let's say we change this text file name here and we change it to something like i don't know errors2 what this will cause is wherever in our code we use this farloga class it will change the file name for all loggers out there and this is often something we don't want we don't want to affect our whole code with just one change so let's just take this errors.txt file as a default error text file but if we want to be able we rather want to be able to to create our own versions of this logger so you could for example imagine that this file logger actually comes from a library and you don't have direct access to it to this class as we do now um then then this really needs to be open for extension so we can actually create our own version of that follower and we can do that by making this class simply open this function as well that just means here we can inherit from that class and then we can create our custom file logo for example our error let's call it custom error file logger and that will now inherit from our file logger class and here in this class we now have the option because we made this log error function open we have the option to override this function and give it our own behavior for this specific custom error file logger so we can say log error we can remove the the super here so we don't call this function if we use this and here we could then use our own text file for example and say my custom error file and if we then want to use a logger at just one place in our code where we want to put the logs inside of this text file then we can do that with our custom error file logger and this now does not affect all the other places where we use these file logos in our code so overall again it says objects or entities which is in the end just a class um they they should be open for extension we should be open for extension here we extend this class but closed for modification so we shouldn't actually modify this stuff here that is what the open close principle tells us the next principle is the l of solid which stands for lisk of substitution and what this principle states is that parent classes should be replaceable by their subclasses and that without altering the behavior so that again sounds super complicated but it actually isn't that much complicated and in the end we already fulfill this principle here in our code because we have a parent class and we have a subclass now let's actually break this rule and break this principle by simply doing something like this custom error log so we now have a custom error log function which does exactly the same as it did before but we don't override the the parent function so why is this actually an issue here it is an issue because now this parent class is not replaceable by the subclass anymore because our subclass intends to actually save the log in this text file but let's say we we have such a file logger instance here and for example in our repository here we don't really know what kind of file logger that is so that could be our custom error file logger that could be such a file logo here it could be something completely different another type of file logo we create here in this repository we don't really know that but in every case we call this log error function and if we now have this custom error log function then this means wherever we just have these file logger instances here like here we wouldn't really consider this custom error log function by using this general log error function because in the end this custom error follower also has that log error function because it inherits from that class so what will happen here even if we pass our custom error file logger here in our repository it will use the log error function of our parent class and it won't use our custom error log function which we actually intended to be used if we have such an error for logger so that is actually a problem and that will often lead to bugs which is why you should prevent that and instead make this an override function for log error and then well we override the the behavior of the parent function so then this function will be used instead the next principle is interface segregation but again sounds very complicated but it's actually the easiest to understand it basically means that um clients so in the end classes should not be forced to implement functions of an interface they actually don't need so let's take a look at an example let's make this file logger here actually an interface interface and this is just a normal function and here we can then implement that interface we don't need to call the constructor that don't really changes anything here for us but let's say this interface now says okay a file logger has a function print logs then that means we need to implement that that print locks function for our custom error follower this is not a problem if we intend that every single file logger we have needs to have this print locks function but in case we don't want this and only some need that and some it's basically optional then we shouldn't force this so let's say we we want to keep this an optional function then we shouldn't leave it like that because that way our customer follower would need to implement that here but if we don't even want to implement that here then we would simply leave this empty function body and that would be bad that just looks ugly in our code so instead you would just give this function default behavior here and then you also don't need to implement it here then you it's basically optional so if we want we can do that we can override it but if not then we don't need to do that and let's get to the last principle which might be a little bit harder to to understand and to spot for that we will leave this file logo file and go back to our main repository take a look at this code again and think about what could possibly be not so good here so we fixed that bug that or not that back we fixed that code issue that we had that this function had multiple responsibilities now it doesn't have that anymore but what if i tell you this class has still an issue so again pause this video for a minute for two minutes check this code and think about what could possibly go wrong here or what is not so optimal so i will give you the solution right now what is not so good is that we use this firebase authenticity here because the last principle the the d which stands for dependency inversion what that states is basically that we should depend on abstractions and not on concretions so what that means is we want to depend on well in abstractions it already says it we don't want to depend on these concretions that is a concrete implementation of authenticating to in this case to firebase but what would happen if at some point in our code or in our project we would decide okay you know let's not use firebase for the authentication let's let's use our own backend then we couldn't really do that with this repository because it forces us to use firebase auth so let's actually check how we can fix that for that we will use an interface that is usually how you fix these things you want to create an interface and an interface is in this case the abstraction so firebase auth is the co is the concrete implementation and the interface the abstraction so let's do that here create another interface now we'll call it authenticator so this interface is called authenticator because it is used to authenticate someone and here we can just say okay we have a suspend function sign in with email and password something like this and let's say this returns an auth result which is the return type here of this of this function well let's let's not do this let's let's remove this auth result we don't need this so now we define an abstract version for an authenticator and we just say okay and each authenticator no matter what that is no matter if it's our own api if it's firebase if it's something for testing that is also very relevant because for testing we don't want to use our real api to run our test cases we don't want to test with the real firebase auth instead we want to have some kind of fake authentication that just simulates the behavior so all we now say is hey each authenticator needs to have this sign in function but each authenticator on its own can decide how the implementation of that looks like so let's see let's implement our class here firebase authenticator and that now implements that interface you can see we now need to implement this function sign oops sign in with email and password and now because we're now inside of the the firebase authenticator we can use the concrete implementation of that and we have to write this basically so here we can now say firebase auth get instance sign in with email and password and we pass our email and password but what we could also now do is we could have another implementation of that authenticator so let's um for example call this our custom api or authenticator and then in here instead of firebase we could use we could make some kind of retrofit call and just make a call to our own api that way if we now take a look at our repository and swap this out with our authenticator interface we can now say okay our repository will just authenticate someone but it doesn't really care if if that's from firebase if that's using a retrofit if that's a fake authenticator basically it just needs to know that it just needs to know the specific authenticator we want to use here we need to remove the weight so if we then use something like dagger hilt and we have something like inject constructor here i don't know can i import this no i don't have that dependency but if we inject something here then we can provide this authenticator instance in our app module and if we need that authenticator instance at many places in our project then all we really need to do is we need to change the one and only thing in our app module and this will change the whole authentication in our whole app with just changing one line of code so all we really then need to do is we need to change one provides function from dagger hilt by just saying hey i don't want to use a firebase authenticator like we did here instead i want to use a custom api authenticator and everything will work exactly the same way as before just that we then use our own api for that so those were the five principles i wanted to talk about in this video i hope this makes it now more clear for you because um when i learned these that was really hard to be honest and it's also totally normal if you don't immediately get these but it is important that you that you actually try to learn these and that you're eager to learn these because these play an essential role when it comes to clean coding in case you want to learn more advanced stuff like this then check out the first link in this video's description where you will find my premium courses these are just the perfect way to to boost your android knowledge to the next level and also of course to support me in my work that i can keep doing all this and videos like this so yeah i'm very thankful for every one of you who gets my premium courses and you will also not regret it i promise that apart from that i wish you an excellent excellent day and i hope i see you in the next video again bye bye
Info
Channel: Philipp Lackner
Views: 14,460
Rating: 4.9836235 out of 5
Keywords: android, tutorial, philip, philipp, filipp, filip, fillip, fillipp, phillipp, phillip, lackener, leckener, leckner, lackner, kotlin, mobile
Id: t8VTLxMsufU
Channel Id: undefined
Length: 18min 24sec (1104 seconds)
Published: Sun Jul 18 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.