Dependency Injection Explained in 7 Minutes

Поделиться
HTML-код
  • Опубликовано: 2 фев 2025

Комментарии • 114

  • @ArjanCodes
    @ArjanCodes  11 месяцев назад

    👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis

  • @loic1665
    @loic1665 11 месяцев назад +63

    Warning, at 4:26: never use a mutable variable as an argument! This is because the same object will be reused if you call the function/method several times!
    This is also a common mistake when you want an empty list.
    A solution is to not provide a default value, but a default factory.
    Apart from this, thanks Arjan for the video :) Very good, as always! The new example is refreshing and is well appropriate

    • @dynamicgrad3820
      @dynamicgrad3820 11 месяцев назад

      What do you mean "when you want na empty list"?

    • @TheEvertw
      @TheEvertw 11 месяцев назад

      ​@@dynamicgrad3820 If you pass an empty list as default argument in a function, that default empty list is created once, and then reused every time that function is called. Which means that most likely, after that first function call, that list no longer is empty. And if the list passed to the function is stored somewhere in your code, there are multiple references to that list spread around your data structure waiting to cause nasty and hard-to-debug side-effects.
      Your Python IDE will warn about this. A better solution is to use an `typing.Optional[List[xxx]]` argument, with a `None` as default argument. `None` is immutable, so it can be safely reused and shared between multiple calls to the function.

    • @TheEvertw
      @TheEvertw 11 месяцев назад +2

      Seconded. I spotted that as well, you beat me to it.
      Besides using the factory argument, Python has the Optional typing hint, which expects a None default value.

    • @dynamicgrad3820
      @dynamicgrad3820 11 месяцев назад +1

      Can someone explain why he added this warning?

    • @erikstv7802
      @erikstv7802 11 месяцев назад

      @@dynamicgrad3820 if you specify a default value to a function argument, Python constructs that object only once (together with the function), not again for every call that needs the default value. A common situation would be that you make an argument default to an empty list []. Let's say that the function's code appends something to this list. You may be surprised to notice that if the function is called a second time, the default value is no longer the empty list but the result of manipulations to the list in the previous function call! I.e. it's reusing the very same instance of the default value, not constructing a new value. A way to solve this is to use None as default value then in the beginning of the function replace None with a new empty list if that's what you wanted.

  • @estevaoyt
    @estevaoyt 11 месяцев назад +8

    Beautifuly explained, clear as water! keep it up!! I think dependendy injection is one of the most important concepts in programming!

    • @ArjanCodes
      @ArjanCodes  11 месяцев назад

      I'm happy you enjoyed the video!

  • @capability-snob
    @capability-snob 11 месяцев назад +7

    Don't get confused when talking to Java programmers about DI. Over there, it generally means "magically inferring which service to use".

    • @cameronmcnz
      @cameronmcnz 2 месяца назад

      So what you mean is "don't worry about the actual definition of the term, but learn something incorrect instead?" Strange take.🤦‍♂

  • @kvicar7419
    @kvicar7419 11 месяцев назад +5

    So in this case, how the email service specific arguments are going to be handled? For example the attachment argument? First we are going to create an instance of email service, pass the arguments to it and then use it to create an instance of EmailSender to actually send the email?

    • @NicolasChanCSY
      @NicolasChanCSY 11 месяцев назад +1

      I was just about to ask the same questions. I thought these would be answered in the video but they are not.

    • @audox4885
      @audox4885 7 месяцев назад +1

      yea that made no sense to me

  • @edgarcarrillo3814
    @edgarcarrillo3814 7 месяцев назад

    Thank you Arjan! Clear, concise, and to the point. I now know Dependency Injection!

    • @ArjanCodes
      @ArjanCodes  7 месяцев назад

      Glad it was helpful!

  • @buchi8449
    @buchi8449 11 месяцев назад +2

    One of the surprising things I encountered when I moved from Java to Python was the heavy usage of the service locator pattern.
    A typical example is Flask's "request" and "d". It is still widespread in Python to put a dependency or an accessor to a dependency in the global context and reference it inside a component. Of course, Python has enough flexibility to manage tests of such less decoupled codes. However, I have always believed that clear decoupling using DI is cleaner and more straightforward.
    FastAPI's DI is handly but covers only part of the DI commonly practised in Java. FastAPI supports only DI in the "request" scope. But DI frameworks in Java, like Spring, support other scopes, including the "application" scope.
    The lack of DI containers supporting the application scope is why people must repeat "app.include_router(...)" etc., to construct big applications. DI realises IoC (inversion of control) at the component level. However, you still need to control the construction of an application (injection of dependencies), as demonstrated in this video.
    The automated construction of applications using DI and CoC (convention over configuration), which is widely used in Java, is something we should consider in Python as well.

  • @maleldil1
    @maleldil1 11 месяцев назад +4

    Why do you use Protocols if you're going to subclass that anyway? Doesn't it defeat the purpose of Protocols (structural subtyping) in the first place?

    • @kaosce
      @kaosce 10 месяцев назад

      Not really. Protocol are better abstract classes

  • @mackymccormack8446
    @mackymccormack8446 11 месяцев назад +1

    Thanks really helpful. In this example though I couldn't see how variations in the email service protocols were catered for. The send_email method didn't show an indication of the different logic needed for different services.

  • @Ruskialt
    @Ruskialt 11 месяцев назад

    Yes it's a great pattern. To actually manage and reduce dependencies you need also consider where to place your abstract classes and interfaces.

  • @bober770
    @bober770 28 дней назад

    In my practice, I've created a class that operates on websocket stream. In order to allow developer to write their own handlers for every possible event thst they could receive from the stream snd avoid unnecessary inheritance of the ehole class, I left to the general websicket class only functions of build up websocket server and parsing messages from stream and passing them to corresponding hsndlers. For handlers i wrote different interface class, that contains all the methods with some basic functionality inside. Developer needs only creates his own child class from interface with his custom logic in handlers and pass this class as a argument to a main controlling class. Inside the methods of this Interface class are reassigned as the methods to the parent class instance. I don't know if it's dependency injection but I hope it is.

  • @danielwhiting4017
    @danielwhiting4017 11 месяцев назад

    Thanks for the great video. I understand that this is a bit of a contrived example but can you explain why you use an EmailSender class at all when it only wraps a method of the service object that is passed to the initialiser? In other words, why not just call the send_email method directly?

  • @ytuserln4990
    @ytuserln4990 11 месяцев назад

    Hello Arjan, great video as usual! I have a question why do you inherit from the protocol in a class which implements one? It's the second video in which I can see this, whereas in ABC vs Protocol you pointed out that it is not needed (duck typing) and also decouples the one who implements from the one who requires. Is there any reason for that? Thank you in advance for the response.

  • @ShadiPL94
    @ShadiPL94 11 месяцев назад +1

    I am bit confused, since when we have to inherit explicitly from Protocols? Your Smtp, SendGrid and MailChimp classes all inherit from EmailService. Or is this just to show that those classes implements the protocol?

    • @Lexaire
      @Lexaire 11 месяцев назад

      They must inherit so that his EmailSender class can call any of them. See code at 4:00

    • @andreas3682
      @andreas3682 11 месяцев назад

      Inheriting from 'Protocol' is necessary to create EmailService Protocol. Whats not necessary and where i believe the confusion stems from is inheriting from 'EmailService'. A protocol in python is a typing related construct and should be used as such by using it to declare the type as done in the 'EmailSender' init method.
      In this example an ABC would be better suited as we have full control over the different implementations. If a third party implementation of an EmailService was imported that you could not control in addition to some of your own code alongside it or as alternative implementations you could use a protocol.

  • @willemvdk4886
    @willemvdk4886 11 месяцев назад +1

    Python has, just like many other languages, very nice libraries for dealing with DI. Very valuable in big projects with loads of modules, units, tests, deployment environments, etc. etc.

    • @amanlodha6260
      @amanlodha6260 11 месяцев назад +1

      Could you share some options here please?

  • @VanosTurbo
    @VanosTurbo 11 месяцев назад

    Agree your codebase gets such an improvement when you start using dependency injection.

  • @roger_rogerthat
    @roger_rogerthat 11 месяцев назад +2

    This time I felt like I've already seen this video. You should talk about an auto wiring DI library like kink

  • @calebrubalema
    @calebrubalema 11 месяцев назад +1

    informative, thank you!
    Have to ask though, is there a Python package that can do DI the way Spring Boot does it?

    • @talwald1680
      @talwald1680 11 месяцев назад

      We heavily use DI in one of our projects, and it is getting really difficult to manage.
      We looked and didn't find anything standalone (FastAPI has DI but not standalone, and the project isn't an http server.. ).
      So if someone knows a good package - lets us know!!

    • @Nalewkarz
      @Nalewkarz 11 месяцев назад

      We have better, something like Lagom for example.

    • @Lexaire
      @Lexaire 11 месяцев назад

      FastAPI or FastDepends have enough DI for most people. But do you mean the Beans? I've not encountered something like that in Python.

  • @chamaocharles
    @chamaocharles 3 месяца назад

    Great video. Thanks Arjan.

  • @ifeanyinneji7704
    @ifeanyinneji7704 11 месяцев назад

    Recently did something like this for work. But used a general email sending function that takes in smtp type, port etc for different email providers.

  • @diegovargas3853
    @diegovargas3853 11 месяцев назад

    Hey Arjan, thank you for your work from Spain. Would be great if you create a video with your top python books "to improve your python skills to another level", thank you!

  • @MagnusAnand
    @MagnusAnand 11 месяцев назад

    These are the best videos!!

    • @ArjanCodes
      @ArjanCodes  11 месяцев назад

      Thank you! Glad you like this content!

  • @danielschmider5069
    @danielschmider5069 11 месяцев назад +1

    Can we see the files email_service and services? You put code there and never showed them

  • @shashishekhar----
    @shashishekhar---- 6 месяцев назад

    Great example.

  • @TheEvertw
    @TheEvertw 11 месяцев назад

    I prefer SMTP as my email sender. Works every time, regardless of how a customer has configured his email server.

  • @heinereniscaicedo7510
    @heinereniscaicedo7510 11 месяцев назад

    There's an awesome third party library called Dependency Injection, it's so good to apply this principle to any python project

  • @Nalewkarz
    @Nalewkarz 11 месяцев назад +6

    Deja vu, You have recorded almost the same video again. Arjan it's time to go deeper into more advanced examples.

    • @ArjanCodes
      @ArjanCodes  11 месяцев назад +9

      This video was a lead up to another one that’s coming very soon, covering DI frameworks 😊

    • @mirkoulrich6312
      @mirkoulrich6312 11 месяцев назад

      @@ArjanCodesi was looking in the comments, if this framework discussion has been requested yet^^. I recently used Dependency Injector, but never got really fond of it. I am looking forward to this upcoming topic.

  • @alexandarjelenic2880
    @alexandarjelenic2880 11 месяцев назад

    I’m confused between the factory pattern and dependency injection. I watched both your videos. Is it the same?

    • @cameronmcnz
      @cameronmcnz 2 месяца назад

      Your confusion is understandable, as what is described in this video is absolutely *not* dependency injection.

  • @hello_world_zz
    @hello_world_zz 8 месяцев назад

    Thanks Arjan

    • @ArjanCodes
      @ArjanCodes  8 месяцев назад

      Glad you enjoyed it!

  • @manonthedollar
    @manonthedollar 11 месяцев назад +1

    Dumb question: In this example, the EmailService ends up hard coded (at 5:17 see Line 6-8, for example). In a more real-world example where you don't know which email service a user will need, won't there still be some big long if/elif/else block that needs to be updated when you add a new service?

  • @JobertoDiniz
    @JobertoDiniz 11 месяцев назад

    5:20 Hi. This is not dependency injection. You demonstrated a pattern called dependency INVERSION. Injection is on top of that. Injection means you outsource the instantiation of a class to a Container that will automatically inject dependencies to the class based on configuration.

    • @ArjanCodes
      @ArjanCodes  11 месяцев назад

      Yes, it is. Dependency injection doesn’t require a container. DI means you provide an object or function to another object or function, thus separating creation and use, which leads to better decoupling. Dependency inversion means you rely on abstractions instead of concrete classes, which I didn’t cover in this video.

  • @vyacheslav_kapitonov
    @vyacheslav_kapitonov 11 месяцев назад

    How will it work if I need to pass an attachment? I still need to change send_email for all of my classes

  • @janetgauntt903
    @janetgauntt903 5 месяцев назад

    To answer your question about which email service, I'm using mailjet

  • @TheEvertw
    @TheEvertw 11 месяцев назад +6

    "Dependency injection", as explained here, is probably the most simple OO design pattern. In OO, it is called the Strategy Pattern. You hide the details of an algorithm behind an (abstract) interface. The user of the strategy only knows the abstract interface, each implementation of the Strategy interface knows only the details it needs.
    I don't understand why people wanted to change the name. The name "Strategy Pattern" has been in wide-spread use for 30 years. I guess people like re-inventing the wheel.
    That is no critique on Arjan, he didn't coin "dependency injection". I thank him for this video, I never understood what people meant with DI. I thought it was some complex trick like the "Curiously Recurring Template" pattern. Which is similar to DI but achieves lightning fast execution & minimal memory footprint, at the cost of long compile times.

    • @m__42
      @m__42 11 месяцев назад +1

      I would say, dependency injection is (usually) built upon the Strategy pattern, but goes beyond that. Your dependencies to inject will usually use the Strategy pattern, but in addition you have some concept of instantiating the dependencies, a repository to keep them, and a way to inject them as needed.
      This was of course the most basic example with just one dependency which is created in the main() and injected via the constructor. Really a textbook Strategy pattern. This works fine for this simple case with only one level of dependencies. However in an even moderately complex application, this simple approach soon falls apart in that it either explodes the number of dependencies you have to pass along, or forces you to create all strategies in a central place which prevents modularization. There Dependency Injection frameworks come to help...

    • @Naej7
      @Naej7 11 месяцев назад +1

      TL;DR : The Strategy Pattern uses the Dependency Injection principle but the Strategy Pattern is not the Dependency Injection principle, and the Dependency Injection principle is not the Strategy Pattern neither

    • @Lexaire
      @Lexaire 11 месяцев назад

      Strategy Pattern is specifically at runtime choosing different options. Dependency Injection is focused on a caller creating an object that the callee acts upon. Strategy Pattern doesn't require generic interfaces. The Before code that Arjan showed was an example of using the Strategy Pattern.

    • @WalterVos
      @WalterVos 11 месяцев назад +1

      All applications of the strategy pattern need dependency injection, not all cases of dependency injection are applications of the strategy pattern. (All swallows are birds, not all birds are swallows)

  • @DanielRodriguez-lu3uu
    @DanielRodriguez-lu3uu 11 месяцев назад

    is decency injection only applicable on Classes?

    • @Lexaire
      @Lexaire 11 месяцев назад

      No, you can inject abstract functions into other functions.

  • @Phoenix_ZA
    @Phoenix_ZA 11 месяцев назад

    I have been happy with Mailtrap

  • @marthinus.x
    @marthinus.x 11 месяцев назад

    Sheesh! Fresh off the presses!

  • @sevdalink6676
    @sevdalink6676 9 дней назад

    Why does this concept look so complicated in libraries like python-dependency-injector? There they use containers and so on and it becomes really difficult to understand.

  • @thefattysplace
    @thefattysplace 11 месяцев назад

    The emailSender class here is not needed imo. A better approach (imo) would be to use a common interface on the sender classes, and use DI to pass the relevant sending class to the interface object. Then just call send on the interface.

  • @jumper0122
    @jumper0122 11 месяцев назад

    I'd really like a few more videos on this topic -- it seems really clever and useful but I don't totally understand it

    • @ArjanCodes
      @ArjanCodes  11 месяцев назад

      I have a couple of older videos talking about it already! :)

  • @PabloVillegasMartín
    @PabloVillegasMartín 11 месяцев назад +1

    I don't get the real advantage of it .The lines of code you reduce in the first class you have to put them in an other file, so you end with the same (probably more) lines, more files and dependencies. I would really like to understand the advantages of it to apply it more naturally.
    Thanks in advance

    • @Lexaire
      @Lexaire 11 месяцев назад +1

      Advantage is for testing, separating construction of a service from where it's used, organizing your services better, and keeping code clean. If you can swap out one service for another without a fuss, then your code is properly decoupled!

  • @deepmodel-q3b
    @deepmodel-q3b 5 месяцев назад

    Isn't using a mutable argument in Python a ROOKIE MISTAKE?

    • @ArjanCodes
      @ArjanCodes  5 месяцев назад

      Unfortunately in Python there’s no way to indicate that an argument is a const. When you pass a mutable data structure like a list as an argument, you probably should avoid modifying the list in the function or method that you pass it to. In case it’s an object with methods that is passed as a dependency, it’s a good practice that reduces coupling.

  • @deViant14
    @deViant14 11 месяцев назад

    I've been doing this but had no idea it was a design pattern.

    • @WalterVos
      @WalterVos 11 месяцев назад

      If you've been doing something (in multiple projects, I assume) then it is by definition a design pattern ;)

  • @PanduPoluan
    @PanduPoluan 11 месяцев назад

    The power of Python's duck-typing leads really well into dependency injection.
    Just pass an object that has the required properties/methods, and done. No need to subclass from an esoteric BaseSomethingClass first.

    • @Nalewkarz
      @Nalewkarz 11 месяцев назад

      Base class is esoteric for You ? 🤣

    • @Naej7
      @Naej7 11 месяцев назад

      Convenient but a bad idea
      Write a Protocol to be explicit, and it’s easier to create a new implementation as you directly know what methods to write with what arguments

    • @darcash1738
      @darcash1738 11 месяцев назад

      Where do the ducks come into play?

    • @Lexaire
      @Lexaire 11 месяцев назад

      You lose the benefits of typing and autocomplete if you don't at least use a Protocol.

  • @MineStrongth
    @MineStrongth 11 месяцев назад

    Reupload? I swear I've watched this before.

  • @kubas89
    @kubas89 11 месяцев назад

    Postmark

  • @crazy_pythonist
    @crazy_pythonist 9 месяцев назад

    hmmmm, is this actually just Strategy Pattern??

  • @KonstantinPrydnikov1
    @KonstantinPrydnikov1 4 месяца назад

    How I hate __init__, self, classes and of course dependency inversion/injection in the code where they don't need at all. They must be used in sending emais where 10+ types of sending and 10+ different services and users that uses that sendings. In all another cases please people uses just def and modules in Python, matherfucka without classes and that oop and solid bullshit

  • @cameronmcnz
    @cameronmcnz 4 месяца назад +2

    Simply passing an argument into a method is not dependency injection. It's passing arguments to methods, something every programmer has done since the start of time. Similarly, using an interface or abstract protocol is not dependency injection. That's simply coding to an interface and has nothing to do with dependency injection. It's no wonder so many people are confused about the term when so many tutorials about it get it wrong.

    • @giantbush4258
      @giantbush4258 3 месяца назад +3

      Disagree. His injecting the dependent object in the constructor. Also emailaservice is an interface thus these this satisfies the "O" in Solid. So I'm not sure what you're taking about.

    • @cameronmcnz
      @cameronmcnz 3 месяца назад +1

      @@giantbush4258 He most definitely is *not* injecting it. He is passing a reference. That's *not* dependency injection. The term DI wasn't invented in 2001 to describe 'passing a reference to a method', something every language could do since 1972. If passing an argument to a method is DI, then doesn't that make pretty much every line of code DI? And thus every class its own IoC container? That's utter nonsense. Trust me, the term wasn't invented in 2001 to describe a basic programming concept kids learn after their first Hello World. DI is not passing a argument to a constructor.

    • @BigDBrian
      @BigDBrian Месяц назад

      You're saying a lot about what isn't a dependency injection, so could you please explain what IS a dependency injection then...?