I have mentored Jr. Developers and wanted to share my experience. Teaching SOLID too early doesn't work because the coder doesn't have the painful memories that mentally reinforce / accurately quantify the value of SOLID. The same goes for recognizing code smells. A Jr Developer is often just pleased with producing a result that works because that's what yielded a good mark in school. (CS degree is a joke IMO. Its like teaching just enough chemistry to make bombs without any safety training)
I am addicted to this channel now. I was searching for something else but when I saw this clip in suggestion, I had to come here as another interesting topic was explained so easily yet again.
Sub earned. Honestly, I looked for an explanation for half an hour and they all just repeated each other. None of them could actually explain it in simple terms like you did.
@@zuzukouzina-original for concepts its better to learn UML which can help you disassociate your self from languages , and focus on understanding concrete ideas not examples
Thanks for the very clear explanation! Depend upon abstraction sounds like a better name. Dependency inversion is a confusing name since store still depends on payment processor not payment processor depends on store.
At first i didn't realy understand how the lower level modules change affect the main code, but when you showed the difference in arguments of Stripe and Paypal it just clicked for me. Great video, thanks!
Has anyone told you that you're awesome? No? Yes? Who cares. Here it is again. You. Are. Awesome. Thanks so much for these videos. You're also making me wanna learn more and more JavaScript.
thanks! Note to self - pass individual implementations as object in the intermediate class (functionality resides within individual implementations and intermediary acts as a glue). this way code is losely coupled and is easy to switch rather than be tightly coupled. Facade pattern eg. is very similar to this. Also Dependency injection in angular / Nest feels similar to this where controller will offload functionality to services).
Some people might legit not like this video. But also a lot of bots dislike videos to appear like real users. When you "buy likes" from an underground service they will spam likes to the video you want, but to appear like they are not bots they will also dislike random videos (to make it seem like it is just organic traffic).
Wouldn't you want just a single PaymentProcessor and have the logic for handling the different integrations within the class itself so you don't have to duplicate the entire class to change a method?
No, you want some interface or set of interfaces for the payment processing, like an abstract class for the Payment Processor, and possibly using something like the Strategy Pattern, to make your concrete Payment Processors. Then you can use the Strategy Pattern to just swap out between different PaymentProcessors easily.
Now this video is old but i dont get one part: Shouldnt there be a Abstract Class or Interface that gets implemented/extended by both processors, so that you dont have to keep changing the payment method in the store? I mean at the moment you still can not allow both methods at the same time and you still have to make changes in your highlevel class for a lower-level change, wich kinda still violates DIP...
a goldmine of information... thank you a ton! just a note... would be great if you can provide a source code for the samples in the video, thanks again
Hi Kyle, good tutorial. thank you. I am little bit confused to distinguish the OPEN/CLOSE and dependency injection based on your example. They are very similar. Can you explain more, please give example
SOLID is Single responsibility Open close principle Liskov substitution principle Interface Dependency injection Richard Martin's said that the S and the O can be join together and create a Dependency inversion principle. So in shorter words, they are the same
For some who didn't know about the origin of DI. It was implemented as "Adapter Pattern" as one of Design Patterns. It was only coined as DI because we don't want to depend our application to the framework. We want the framework to depend our application.
@@FG-qs8uj If you want to know more why DI is so important in large scale application. try to search up Clean Architecture by Uncle Bob. It's just the same as SOLID principle. If you're not happy with OOP. then youi can always go back to Functional Programming.
If you read what you wrote: We don't want to depend X to Y. We want Y to depend X. X is application, Y Is framework. Wouldn't be better: We don't want X to depend on Y. We want Y to depend on X? Or: We don't want Y as a dependency of X We want X as a dependency of Y. In any case, my point is that saying X depends on Y is more intuitive to me. I.e. an application depends on a framework to work, i.e. if you see the application requirements file you would see the framework mentioned. The other way, Y depends on X. Meaning the framework depending on the application is not intuitive to me, since the framework doesn't need the application to work. I suppose then that the semantics of 'depend' and derivative words is what is confusing. And even then, I don't see the point on discussing Y depending on X Vs X depending on Y. At the end I don't see what changes in the code.
Why not make StripePaymentProcessor and PaypalPaymentProcessor implement an Interface PaymentProcessor to enforce the structure of the makePayment method? Optionally it could also extend a PaymentProcessor abstract class if there is some shared method.
Honestly i didn't catch the point of doing that in this example. For me it looks like instead of 2 store classes for each payment method we have just created 3 classes (1 store and 2 payment processors). I thought it should be one payment processor class for both cases which will react different depends on arguments we passed either paypal or stripe. So, a little bit confused by this lesson.
The point of this code is so that we can decouple the API we use and the store from each other. Now our store only depends on the processor wrapper we created so we can freely change our API of choice by just creating a new wrapper to wrap that API and our store stays exactly the same. If we tried to make one payment processor that handled all the APIs then we would be violating other SOLID principles such as Open/Closed.
Thank you so much for this great tutorial. I think it would be better if you created an interface with function called pay and let the processors implement thisbfunction
Imagine then that PayPal introduces coupons as a payment functionality that we can now use. Would we then put the extra functionality in the PayPalPaymentProcessor and let the PaymentProcessor API stay as is, or would we have to redefine the API(s)? I myself tend to like the idea of putting the incommon functionality in the implementations themselves and keep the common API for common functionality, but I am not sure whether or not that follows SOLID well enough.
Thanks!!!! I have a question, how to make sure that both StripePaymentProcessor and the other one have pay method? By implement Interface is good idea???
a base PaymentProfessor interface that enforces makePayment() method is a good idea, since it is reasonable to assume that all payment methods will make payment, so other the substitute principle is satisfied also.
Isn't the wrapper dependent on the payment API and not the other way around? so you're just adding an intermediary class in-between to essentially normalize the differences between the different payment APIs?
Does this mean we need to create middleman classes/functions whenever we have dependencies on external third-party api's? Should we do the same if we have have dependencies on internal modules(if it is a big/long module) we ourselves created?
Coupling is only bad in one direction. Also don't couple high level business code / computation code with low level data storage / access code. Uncle Bob is probably the most famous SOLID guy. Checkout some of his videos if you are interested in further developing your understanding of SOLID.
There are no interfaces in Javascript? How can you be sure that each type of *PaymentProcessors has method 'pay'? Or should you check it manually in Store class?
Simple version: Check that the pay method exists before setting this.paymentProcessor. if (typeof paymentProcessor.pay !== "function") { throw new Error('Store.paymentProcessor requires method pay ' ) } Sophisticated version: Create a NullPaymentProcessor class that contains the required methods, but they throw a descriptive error (or handle the exception some other way, depending on your implementation) , then make StripePaymentProcessor and PayPalPaymentProcessor extend the NullPaymentProcessor class. class NullPaymentProcessor { pay() { throw new Error(`No pay method defined in ${this.constructor.name}. pay is a required method`) } otherMethod() { throw new Error(`No otherMethod defined in ${this.constructor.name}. otherMethod is a required method`) } } class StripePaymentProcessor extends NullPaymentProcessor { constructor(user) { super() this.stripe = new Stripe(user) } } class Store { constructor(paymentProcessor) { this.paymentProcessor = paymentProcessor } purchaseBike(quantity) { this.paymentProcessor.pay(200 * quantity) } } const store = new Store(new StripePaymentProcessor('John')) store.purchaseBike(1) output -> No pay method defined in StripePaymentProcessor. pay is a required method More or less, use the null object design pattern to implement an interface in javascript. Kyle, would you agree with this approach?
But what is the point of making two abstract classes instead of one? Make one, make a init function, a makepayment function and the paypal/stripe fully inherit that and you define what you want. The only thing that happened is that instead of defining a new entity Paypal and using the function, you added a new class which calls the subclass.
Kinda funny. My company is currently implementing Stripe into our infrastructure. The project has taken 8 months. I’m QA. No real visibility to the code. But it must be breaking multiple design principles to take this long.
This kind of stuff screams for the use of interfaces (I know they are not supported in JS, one of the reasons I moved on to TS) In this case it's the responsibility of the developer to make sure all paymentProcessors implement -correctly- the method pay
This is exactly what I was thinking. Programming without interfaces always looks so cumbersome. This could have been resolved in a much simpler way with an interface.
Hmmm not sure! Dependency inversion is a type of dependency injection. What is special about this kind of DI is that you do not declare in any moment what concretion will be used inside the high level classes, it leaves it to the client code in which the high level class will be instantiated. Not any dependency injection works like that, after all it's possible to design a dependency injection declaring a low level class inside a high level class, which violates the dependency injection principle, making the high level class get to meet the low level class, generating coupling.
Dependency Inversion is a form of abstraction, but not all forms of abstractions are Dependency Inversion. Basically, you use DI when you're implementing something that can be done in multiple different ways and you may need to change/support other ways in the future. Besides the Payment Processor example in the video, another example would be if your app had a "share to social media" feature. Say you only wanted to have an option to share to facebook now. You could make a facebook class & shareToFacebook() method that shares your content to only facebook. But now if you decide to add options to share to twitter or instagram later, you're going to have to edit multiple places in your code base to support this. Now imagine if instead of creating a facebook class & shareToFacebook() method, you created a SocialMedia class and a generic share method that can be passed in a specific type of social media. Now you only have to extend the SocialMedia class to support twitter, instagram, etc and you don't have to go and edit your whole code base like you would in the facebook class example. Hope this makes sense, good luck!
@@WebDevSimplified Your videos are still awesome and "simplified" :) keep them coming. Video suggestion: A JavaScript learning roadmap which gets more complex the more you advance (Junior, mid-level and senior). As most roadmaps out there focus more on the web-dev path in terms of tools and technologies, but non goes in depth in terms of the language itself and CS concepts.
In practice these abstractions make code difficult to read and navigate: you keep getting to abstract interfaces and then having to back reference and sometimes guess which implementation is used. The whole idea is that we may easily swap out implementations but in practice this is surprisingly rare.
Depends on the kind of software you write, sometimes it's very useful. It also facilitates decoupling which can reduce build time in compiled languages and makes it easier to unit test your modules separately which is in my opinion the real reason to do this.
funny enough, we you experience pain in coding and slowly walk your way out for cleaner code, you might unconciously applied SOLID principle without knowing it