Dependency INVERSION vs Dependency INJECTION in Python

Поделиться
HTML-код
  • Опубликовано: 19 июн 2024
  • What's the difference between dependency inversion and dependency injection? Dependency injection is a design pattern that splits creation of objects from their usage. Dependency inversion is a design principle that promotes using abstract classes and interfaces to better separate code. Watch this video to see a few practical examples in Python, and how these concepts help you write nice code that's easy to test.
    The code I worked on in this example is available here: github.com/ArjanCodes/2021-de....
    💡 Here's my FREE 7-step guide to help you consistently design great software: arjancodes.com/designguide.
    🎓 Courses:
    The Software Designer Mindset: www.arjancodes.com/mindset
    The Software Designer Mindset Team Packages: www.arjancodes.com/sas
    The Software Architect Mindset: Pre-register now! www.arjancodes.com/architect
    Next Level Python: Become a Python Expert: www.arjancodes.com/next-level...
    The 30-Day Design Challenge: www.arjancodes.com/30ddc
    🛒 GEAR & RECOMMENDED BOOKS: kit.co/arjancodes.
    A few interesting links to articles and books:
    - Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides: amzn.to/3jllgyH
    - Principles of Package Design: Creating Reusable Software Components by Matthias Noback: amzn.to/2NETK3l
    - Clean Code: A Handbook of Agile Software Craftsmanship by Robert Martin: amzn.to/3qVZgNs
    - The original Design Principles and Design Patterns article by Robert Martin: fi.ort.edu.uy/innovaportal/fi...
    👍 If you enjoyed this content, give this video a like. If you want to watch more of my upcoming videos, consider subscribing to my channel!
    💬 Join my Discord server here: discord.arjan.codes
    🐦Twitter: / arjancodes
    🌍LinkedIn: / arjancodes
    🕵Facebook: / arjancodes
    🔖 Chapters:
    0:00 Intro
    0:20 What is a dependency?
    1:10 Dependency injection
    1:36 Dependency inversion
    2:03 Example code
    3:22 Example unit tests
    5:32 Issues with testing
    6:37 Applying dependency injection
    8:35 Writing the unit tests
    11:20 Applying dependency inversion
    12:51 Coverage reports and abstract classes
    14:16 The power of dependency inversion
    14:49 Adding another authorizer
    15:44 Adding tests for the Robot authorizer
    17:10 Summary
    #arjancodes #softwaredesign #python
    DISCLAIMER - The links in this description might be affiliate links. If you purchase a product or service through one of those links, I may receive a small commission. There is no additional charge to you. Thanks for supporting my channel so I can continue to provide you with free content each week!

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

  • @ArjanCodes
    @ArjanCodes  3 года назад +78

    Finally upgraded to better microphones for this video!

    •  3 года назад +5

      It makes a huge difference!

    • @jamie9926
      @jamie9926 3 года назад +1

      You could record an audiobook about python now haha!

    • @metal571
      @metal571 2 года назад

      The RE20 is an excellent choice. Love this channel

    • @alira7296
      @alira7296 2 года назад

      I'm only a couple of minutes in so far, but I'm actually finding it hard to listen because of the music...

    • @raidensama1511
      @raidensama1511 2 года назад

      It’s about time 😉

  • @susmitvengurlekar
    @susmitvengurlekar 3 года назад +78

    After learning about testing, there is more happiness and more smiles when all tests are pass than the actual code working properly 😂

    • @ArjanCodes
      @ArjanCodes  3 года назад +15

      Who cares about working code! 😄

    • @vekyll
      @vekyll 2 года назад +4

      If you write your tests properly, these two should be the same!

    • @yuriysukhorukov391
      @yuriysukhorukov391 2 года назад +7

      @@vekyll The testing can not prove that the code works properly, it can only prove that smth goes wrong )))

    • @vekyll
      @vekyll 2 года назад +5

      @@yuriysukhorukov391 Of course. This does not contradict what I said.

    • @digitig
      @digitig Год назад

      @@vekyll You can't fully test any non-trivial code. That's why for critical software there has to be analysis as well.

  • @HenockTesfaye
    @HenockTesfaye 2 года назад +14

    I like the background sayings
    "It depends"
    "Solid advice"

  • @ruszkait
    @ruszkait 2 года назад +76

    Very nice video again! :-) Dependency injection is covered very well, but the dependency inversion was for me a bit incomplete. As I have seen you have just implemented a "strategy pattern". In my opinion dependency inversion is a bit more than that - though the machinery is the same. The essence of dep. inversion: We want to make that the peripheral details depend on our precious business logic and not the way around. In you example I see the Order and Payment Processor as the business logic, which we want to protect from peripheral details. Therefore we split the Order and Payment Processor into a separate module and there - beside the Payment process implementation - we define the Authorizer interface (ABC in python terms). So the business logic component is "virtualized", it runs on an abstract peripheral - as we have only an interface for the peripheral. Then in a Authorizer module we implement the Authorizer interface from the business logic module. If we do so, then we have flipped (inverted) the dependency. The business logic (Order, Payment Processor, abstract Authorizer interface) does not depend on the concrete peripheral implementation any more, it depends only on its internal interface (Autohrizer interface). On the other hand the Authorizer (peripheral) depends on the business logic as it implements the Authorizer interface. So just by shifting the interface from the peripheral component to he business logic component we have flipped (inverted) the direction of the dependency between the components. I think it is hard to demonstrate it well if all the logic is in one file - you do not see the component (module) boundaries. It would easier if you split up the code: the business logic component (python module) would contain the Order,Payment Processor, Authorizer interface and the peripheral component would contain the Authorizer implementation classes. So the "import" statements would reveal the real power of dep. inversion. You would have an "from business_logic import Authorizer" in the peripheral code, but in the business logic you would have no "from Authorizers import SMS_Authorizer" statement. Another improvement could be that the composition root is also in another component (this is what you have now in the main funcion). The composition root would wire everything together so, it would be dependent on everything (business logic and authorizer peripherals).

    • @ArjanCodes
      @ArjanCodes  2 года назад +37

      Thanks for your feedback! It's true that to get "real" dependency inversion, you'd split out the different classes into different files. I have not done that for these examples to keep things simple, but in this case it might have been helpful to do that. I'm trying to ride a careful balance between having practical examples that actually show how it works in "the real world" but still keeping things simple enough that my videos about them make sense :).

    • @Victorinoeng
      @Victorinoeng 2 года назад +13

      Thanks, Tamas. Your comment was polite and constructive! I am actually gaining twice the value: from the videos and the amazing constructive comments, such as this one!
      The idea of the importing statements made a lot of sense to me regarding the “inversion” part!
      Cheers!

    • @xtunasil0
      @xtunasil0 2 года назад +6

      I was just asking myself "ok but why is it an 'inversion'?" And your example is crystal clear!
      Thank you very much!

    • @magenertech9412
      @magenertech9412 2 года назад +1

      Great video and great comment, just to get things straight (correct me if I'm wrong):
      Dependency Inversion is injecting generic abstract dependencies rather than the actual dependencies.
      This makes the actual implementation of a said dependency irrelevant to the dependent class therefor making looser coupled code.

    • @ruszkait
      @ruszkait 2 года назад +19

      @@magenertech9412 What you describe is rather the dependency injection: where you want to consume a class, you consume an interface instead and the instance is given/injected to your class. For example if you want logging in your library, then you expect in your constructor arguments a logger interface. Then when the system starts-up it can decide what logging system will be injected. For your this detail is irrelevant, you just use the interface. The dependency inversion is a bit different topic: It helps you to place the interfaces (like the logging interface) into libraries. In the classical approach you have a logging library, which has a logger interface. If you want to use logging via that interface, then you become dependent on that specific logging library. So basically your flexibility is reduced to a certain level, that you do not have to know which exact logger from the logging library is injected into your code (say: network logger, file logger, console logger), but you are limited only to those loggers, which are coming from that specific logging library. If you do a step forward and you want to get rid of this restriction, too, then you apply the dependency inversion principle. In this case you do not use an interface from the logging library but you declare and interface in your library yourselves. Then in the system code (say the main function) the user of your library creates a small adapter class, which maps between your logging interface and the interface of the logging library. This way you became absolutely independent of any logging framework, your library is much more versatile. And where is the inversion? Now your library is not dependent on the logging library, so you do not have outgoing dependency, but the adapter becomes dependent on your library. So you have flipped the direction of the dependency just by pulling the interface into your library.

  • @steventimberman9313
    @steventimberman9313 2 года назад +31

    This is the first example I've ever seen that clearly shows the motivation behind an abstract class, great video!

    • @ArjanCodes
      @ArjanCodes  2 года назад +2

      Glad you liked it, Steven!

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

      I usually refer to databases when thinking about dependecies. Your project will likely be just fine if it uses mariadb or postgres or oracle or mssql.... Just provide an object that can run basic SQL queries, doesn't matter which.

  • @AlejandroLamKhoa
    @AlejandroLamKhoa 2 года назад +1

    Thank you for having the videos in this series build on concepts of earlier videos. I have been binging your videos, and now while following your example of dependency inversion, I could already tell how you were going to apply that concept and the code you'd write. Thank you Arjan!

  • @SurajAdhikari
    @SurajAdhikari Год назад +14

    Nice video. One important detail you could add for tests is that instead of instantiating an actual object we normally create a mock object which inherits the abstract class. The mock implementation obviously will have dummy return values. But that will highlight the true power of dependency injection in lieu of unit testing.

    • @ArjanCodes
      @ArjanCodes  Год назад

      Great suggestions, Thank you.

  • @WilliamBrumble
    @WilliamBrumble 2 года назад +4

    Well structured thorough video that includes design pattern and unit testing. Thank for sharing!

  • @kukuruzayevhenii8764
    @kukuruzayevhenii8764 Год назад

    Thank you! I watched a dozen videos on the topic, but this one not only explained it in the best way, but also felt like a way more complete explanation!

  • @AlexGb007
    @AlexGb007 2 года назад +4

    Keep up the good work Arjan. These Python OOP topics are helping me understand a lot of new concepts! I also like how you demonstrated unittests and code coverage!

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Thank you Ali, and will do!

  • @cetilly
    @cetilly 3 года назад +17

    Your videos are brilliant. Just love this content. I need to watch this video a couple times to catch everything going on.

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Glad you enjoy it, Chuck!

  • @fazzah777
    @fazzah777 2 года назад

    your test cases with repeat initialization of sms authorizer are a great starting point for a pytest fixtures tutorial, which are a powerful tool as well. Great video, as always!

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Thanks! And that's certainly something I'm going to cover soon on the channel!

  • @davidzitzmann542
    @davidzitzmann542 2 года назад

    6:24 I'm glad that I'm not the only one having problems with this mock system. I had such huge problems with this, that I was forced to use dependency injection. Thank you python standard library to make me a better person!

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

    Fantastic Tutorial, you explain why something needs to be done instead of just explaining how it's done(which are most of the tutorials in RUclips). And it's very easy to retain the knowledge because now you know why's behind the logic. Great job !!

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

      Much appreciated! Thank you very much!

  • @RichardVodden1
    @RichardVodden1 2 года назад

    This is a wonderfully clear explanation of a potentially very complex topic. Thank you very much. I tend to prefi my instance variables with _ (e.g. self.autborizer) so that mypi will warn if I call it from outside the class. This avoids accidentally introducing coupling through injected components.

  • @taylormonacelli
    @taylormonacelli 2 года назад

    So clear, so concise, so awesome! Thank you Arjan

  • @vinnyt4398
    @vinnyt4398 2 года назад +2

    I've also encountered similar pain when running tests where my function creates an object within, and I need that object to be in a certain state in order for my test to pass. The thing I had to do (not really recommended, but it works), is you use mock on the "__init__" method of the class for the object you create. In your example here, you would have to mock the "__init__" method of the class Authorizer_SMS

  • @nithyanandhasubramanya
    @nithyanandhasubramanya 2 года назад +1

    Wow !! Just one video and I subscribed . The content and quality is outstanding !!!! thank you so much for sharing the knowledge in such a easy way. I look forward to more such videos.

  • @user-tq7xp6ev1y
    @user-tq7xp6ev1y 2 года назад +1

    Thank you for really clear and helpful explanation. Code examples are very illustrative too. Nice work!

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Thank you - glad you like it!

  • @andyanderson222
    @andyanderson222 Год назад

    I absolutely love this channel! Its pure joy to watch these tutorials! Thanks a lot!

  • @rjgonzalez8108
    @rjgonzalez8108 2 года назад

    Thanks for sharing this high quality content. Very clear explanations. I'm learning Python coming from an embedded systems background. I need to pickup the language asap. I'm currently debating on whether to pay for.a course or teach myself with books, videos and doing projects. Would be great to know how you or others reading this have learned Python.

  • @NilenduDasnilendudas
    @NilenduDasnilendudas 2 года назад +1

    Dependency inversion is the process of introducing a new layer between objects in the form of abstract class to reduce dependancy between objects directly and also means that these can be replaced with their subtypes. - This is hands down the most simple definition I have ever heard. Thank you so much.

    • @DerekHohls
      @DerekHohls Год назад

      Are ABC classes really the only way to achieve this?

    • @janekschleicher9661
      @janekschleicher9661 Год назад

      @@DerekHohls In python certainly no. You can just duck type and just call the methods on the passed objects. As the method call is resolved dynamically at runtime, it works if you pass the right kind of object and most certainly fail if not. The only disadvantage of that approach is that it makes IDE/linting support harder, so less likely to have autocompletion or detection of completely wrongly passed objects before starting the program. (You can work that out by helping the linters with specific mypy or pylint or w/e header files for the objects or improving the IDEs. For several highly dynamic projects that uses dependency inversion/injection a lot like Django, that's indeed what's done there). And of course, what also can happen that for accident the passed object is the wrong one, but supports the called method, so your business logic is doing something completely wrong just because two different methods are called the same for different classes (this of cause concerns the purist, but is rarely a real problem).
      The main motivation for the abstract classes is coming from the static compiled languages that just need to know an interface to check it at compile time and prepare the calling in the compiled code already (internally they also reserve some at compile timed known locations like offsets where the address of the method is stored, so they can just say at assembly JUMP to that address). But even in those, big dependency inverse/injection systems are often implemented by (what is there called) reflection what is just dynamical lookup with a bit more type safetyness. The classical example for it would be Java Spring, but most other bigger Java systems use this kind of reflection a lot, too. The most negative example would be log4j where this resulted in a hazard security problem :-) - But indeed, the pattern can also be completely language agnostic. E.g. Kubernetes uses it also to a high degree and with just configuration in yaml files (again bypassing a very static language like Go with a very dynamic injection system).
      So, the abstract classes are the old school examples coming from old Java in the design patterns written in the 90s. They are still valid, as unless you want to write a big framework, in static languages, they are often enough choosen as path of least resistance and "most" control. But certainly, the dependency inversion/injection is not necessarily achieved by abstract classes and I guess (I don't have hard numbers on that), that most big system don't rely on it per se. (Usually they still have it, even though it's technical not necessary, but as a way to communicate the necessary interface and it's easier to write an abstract interface/class and have an auto documentation, auto linting, auto completion, etc.) - than to "just" write a good documentation and a good error handling if things are not done right :-)

  • @romulorff
    @romulorff 2 года назад +2

    Awesome video and very illustrative examples, love it! I noticed you were running Python 3.9 so I would have also consider to use Protocols instead of ABC, which was introduced in Python 3.8

    • @ArjanCodes
      @ArjanCodes  2 года назад +3

      Thank you! I’m working on new examples at the moment that rely on Protocol instead of ABCs.

  • @pinakadhara7650
    @pinakadhara7650 2 года назад

    Excellent content. Thanks a lot for making this!

  • @paulkuhne3855
    @paulkuhne3855 2 года назад +1

    Extremely clear explanation. Great! Thank you!

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Glad you enjoyed it, Paul!

  • @rolandovillcaarias5112
    @rolandovillcaarias5112 Год назад

    Finally I can see an example to understand DI clearly. Thank you Arjan for your explanation :)

  • @Hubert4515
    @Hubert4515 2 года назад

    super useful information about inversion and injection, thanks

  • @dmytroparfeniuk2670
    @dmytroparfeniuk2670 2 года назад

    Best video I have seen regarding DIs. Thanks!

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Glad it was helpful, Dmytro!

  • @paulorugal
    @paulorugal 2 года назад

    Hey Arjan! Recently I've been diving into your videos, they're so amazing!
    Your knowledge is very SOLID and the references and arguments you use are very clarifying!
    Thank you for posting such amazing content!
    I started using python in 2011 and at that time people would frown when the subject was threads.
    Most of my peers would use Java saying the support for using threads was a lot better, do you think/plan on touching this subject?
    And/or even making some comments explaining if threads are helpful in the web development context or a microservices approach would be a better approach

    • @ArjanCodes
      @ArjanCodes  2 года назад +5

      Hi Paulo, thank you - I'm glad you're enjoying the videos! I must admit I haven't used threads in many years. When I need asynchronous behavior, I'll generally use asyncio/Futures. But I can imagine there are still quite a few types of applications where threads are important (I just don't happen to work on those :) ). Async is a topic that's on my video list, so I'll surely cover that on the channel at some point.

  • @MarkWernsdorfer
    @MarkWernsdorfer 2 года назад

    thanks for the video! instead of a docstring or a rc file, can't you also just raise notimoplementederror for coverage to ignore abstract classes? or decorate them as abstract?

  • @ComputerScienceSimplified
    @ComputerScienceSimplified 3 года назад

    Incredible video, keep up the awesome work! :)

  • @brennorris7061
    @brennorris7061 Год назад

    Coding Away, one of your video came up on in the background... 3 hours later, i think you're one of the best presenters on RUclips :) Very knowledgeable and not afraid of diving down deep. Thank you for all your time and effort! PS i totally hate the python module system of 3.7 (we have some legacy code and we cant get off the damn version) - are there notable improvements between 3.7 and say 3.9 or even 3.10?

    • @brennorris7061
      @brennorris7061 Год назад

      OH! and if you even see the above comment I have to ask: are you able to target bytecode (WHL/Eggs?) written in 3.9/3.10 so we can write modules in newer versions and know they'll work on the older 3.7 codebase? i'm pretty new to Py - sorry if its a stupid question.

  • @brianakong7644
    @brianakong7644 Год назад

    Awesome video, explanations, and examples! Thank you!

    • @ArjanCodes
      @ArjanCodes  Год назад +1

      Thanks so much Brian, glad the content is helpful!

  • @mpete0273
    @mpete0273 3 года назад

    Amazing videos - keep it up!

  • @felipealvarez1982
    @felipealvarez1982 2 года назад +2

    I was delighted to see mock and testing! Every programmer should be doing this!

    • @DerekHohls
      @DerekHohls Год назад

      I missed seeing mock objects here - where were they used?

    • @felipealvarez1982
      @felipealvarez1982 Год назад

      @@DerekHohls I think at 4:06 and 9:49

    • @DerekHohls
      @DerekHohls Год назад

      @@felipealvarez1982 Those snippets refer to "patch" not mock?

    • @felipealvarez1982
      @felipealvarez1982 Год назад

      @@DerekHohls patch is a convenience method for mock. Same library.

    • @DerekHohls
      @DerekHohls Год назад +1

      @@felipealvarez1982 Thanks! I find mocking quite an obscure topic to understand; but Arjan's use of it here made perfect sense. Hope he tackles the topic in depth at some point.

  • @danspeed93
    @danspeed93 2 года назад

    Thank you that is a nice video that goes directly in my favorites

  • @AR-scorp
    @AR-scorp 2 года назад +1

    Excellent video. Thanks for making and sharing this.

  • @theunknown2090
    @theunknown2090 2 года назад

    Great video, more people should start teaching like you.

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Thank you, happy you liked the video!

  • @gcl2783
    @gcl2783 2 года назад

    I like the tests over the driver scripts of previous videos.

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

    superb.
    what should be the guideline to splitting it properly?

  • @LucasNaruto8107
    @LucasNaruto8107 3 года назад +1

    Your content is amazing m8!

  • @dariuszspiewak5624
    @dariuszspiewak5624 Год назад

    Arjan, is it not more convenient to use pytest instead of unittest? Seems to be simpler and (maybe?) more flexible?

  • @robertbrummayer4908
    @robertbrummayer4908 2 года назад

    Great explanation

  • @johnrperry5897
    @johnrperry5897 3 года назад

    I love your lighting!!

  • @douglaspearson5741
    @douglaspearson5741 3 года назад

    Great Channel - Learning a lot

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Thank you Douglas, glad you are enjoying the videos.

  • @davidlayton5054
    @davidlayton5054 2 года назад +1

    Enjoyed the video. The patches are a smell for injection. It felt like you were going to inject input. Was that cut in the interest of time? It opens up so much extensibility.

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Ha, well spotted. I did cut a few things out to keep it from becoming too long.

  • @Roman-kn7kt
    @Roman-kn7kt 10 месяцев назад

    Awesome!!!

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

    You are the coolest developer ever

    • @ArjanCodes
      @ArjanCodes  Месяц назад +1

      Thank you so much for the kind words!

  • @ice7mayu
    @ice7mayu 2 года назад

    Thank you , ur vid is very helpful

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Thank you, glad it was helpful!

  • @vikingthedude
    @vikingthedude 2 года назад

    Hey Arjan, would you recomment injecting and mocking the builtin "input" function as well? How do you decide when to inject and when to just directly use an object/function?

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Hi @VikingTheDude, I'm going to release a video soon where I talk about unit testing and in particular patching and mocking. So stay tuned :).

  • @JabmarMusic
    @JabmarMusic 2 года назад

    Thanks for the video. Can you tell me what Color Theme do you use?

  • @sebastianrodriguezcolina634
    @sebastianrodriguezcolina634 2 года назад +2

    Hey thanks for your videos, they are great. I have a question for this one. Is there any reason you use things like is_authorized and set_status instead of using the property decorator?

    • @ArjanCodes
      @ArjanCodes  2 года назад +3

      When I published this video I was not yet fully committed to Python-only on my channel, so I wanted to keep the more Python-specific things such as decorators out of the examples for simplicity. I've now moved to fully focus on Python in my channel, so you can expect to see these kinds of things appear more frequently in my upcoming videos.

    • @sebastianrodriguezcolina634
      @sebastianrodriguezcolina634 2 года назад

      @@ArjanCodes thank you! Great work 👌🏼

  • @muralir8504
    @muralir8504 2 года назад

    best tutorial for advance topics

  • @geekyboytop
    @geekyboytop Год назад

    Awesome explanation thank you took plenty of notes!

  • @_hello_5346
    @_hello_5346 Год назад

    This is great, thank you.

    • @ArjanCodes
      @ArjanCodes  Год назад

      Thank you, glad you liked the video!

  • @ChrisHalden007
    @ChrisHalden007 2 года назад +1

    Great video. Would be nice to use bigger font. Very hard to watch full screen on mobile phone.

  • @iliqnew
    @iliqnew 2 года назад

    AMAZING VIDEOS! THANKS!

  • @willz81
    @willz81 Год назад

    Is there a blog post explaining dependency inversion in relevance to file structure?

  • @demidrek-heyward
    @demidrek-heyward 3 года назад

    great content thank you

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

    Is this abstract class or "intermediate layer" as you call it an interface? If not, what's the difference between such an abstract class and an interface?

  • @davidlayton5054
    @davidlayton5054 2 года назад

    In your unit test you use a real authorizer instance rather than a spec'd mock. I've always used mocks to reduce coupling in tests, but that often convolutes the test and sometimes the mock behavior diverges over time. So I can see the merits of using the real instance. What are your thoughts?

    • @ArjanCodes
      @ArjanCodes  2 года назад

      I didn’t use a mock for the authorizer since it is a really basic class (not much more than a mock actually). If a class is relatively simple and is more or less standalone, I don’t have any issues using instances of it directly in the test. But then I wrok at a startup where we have to move fast sometimes, so that means making sacrifices once in a while.

  • @grif.n
    @grif.n 17 дней назад

    Is there a reason the PaymentProcessor needs to take an authorizer argument at all instead of there being another process_order method on the Order class which authorizes the user and then takes payment? Is it because payment shouldn't be possible without authorization every time, so the strict usage of authorize inside pay method is necessary?
    Would refactoring authorize and pay to happen separately but inside a process_order method also be dependency inversion?

  • @dawid_dahl
    @dawid_dahl 2 года назад

    Thank you! 💙

  • @miquelvazquez4544
    @miquelvazquez4544 2 года назад

    This is really good! But a subtle point: DI/IOC is possible not only with classes but in functions. The theory is the same, the issues and benefeits are the same as well :)

    • @koraytugay
      @koraytugay 2 года назад

      Are you talking about callbacks? How do you do DI in functions?

    • @miquelvazquez4544
      @miquelvazquez4544 2 года назад

      @@koraytugay By passing the dependencies as parameters. `def notify_user_signed_up(user: User, email_service: EmailService, jwt_service: JwtService)`

  • @Jacob011
    @Jacob011 2 года назад +3

    Turns out, I considered this common sense and used it in my code without even knowing what it is called.

  • @wing-keiwong9418
    @wing-keiwong9418 Год назад

    It looks like generate_sms() is not called as part of the new code. How will you retractor the codes so it is included when the class authorise_sms is used and excluded while authorise_robot is used?

  • @nikolaikruglikov7358
    @nikolaikruglikov7358 2 года назад +2

    I’ve got a question: how do you decide where to put a low dash (_) in a class name? It seems that you use it as an indicator of inheritance, but it would be nice to know an exact heuristic.
    Thanks for your videos! I would not call myself a novice, but I am still picking something new from each video on this playlist :)

    • @TechLord79
      @TechLord79 2 года назад +1

      AFAIK Python is more in the "snake-case camp" ie. this is just pure convention regarding how to name things compounded of multiple words, eg. value_added_tax for a variable name in Python vs. valueAddedTax (camel-case) in C#. However, as I see from PEP8 Authorizer_SMS is not strictly to spec - it should be "CapWords", ie. AuthorizerSMS, also named Pascal-case, which eg. C# also uses for class names. (As an aside in the .NET/C# world there is also discussion about acronyms > 2 characters being cased, eg. FileIO but HttpContext not HTTPContext; though in Python, PEP8 takes a different stance: "When using acronyms in CapWords, capitalize all the letters of the acronym. Thus HTTPServerError is better than HttpServerError.")
      www.python.org/dev/peps/pep-0008/#naming-conventions
      To sum it up: Although Python has many aspects using conventions instead of language features (like leading _ or __ for internal/"private" names) in this case the class name has no special technical meaning, just personal preference/intuition of the author. HTH.

    • @ArjanCodes
      @ArjanCodes  2 года назад +2

      Great question, thanks. @TechLord79 already gives a pretty good overview of naming conventions in Python. I did this video already a while ago and I've since moved to a more strict following of Python's naming conventions. There is no particular reason to use the underscore here, you can just as well leave it out.

  • @pascalpfeiffer9088
    @pascalpfeiffer9088 3 года назад

    Another great video!
    Where can I get the picture from the background?

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Thanks Pascal, that's just an image I created myself using a photo from Unsplash + added the two colors. If you send me an email at business@arjancodes.com, I'd be happy to share it with you.

  • @phaphiq
    @phaphiq 3 года назад

    Very good video, I will check the other you have now :-)

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

    As ever, an excellent video but could I just recommend that watchers follow this up with Code Aesthetic's equally excellent Dependency Injection video.

  • @esatylmaz1476
    @esatylmaz1476 2 года назад

    hello, authorizer is abc and has common functions, but when I give the sms method to Payment Processor, and intellisense like authorizer.get_sms_code() does not appear, how can I provide this?

  • @sashacooper9326
    @sashacooper9326 2 года назад

    As primarily a Rubyist, this feels like a halfway house to me between type checking and duck typing. I'm a big fan of duck typing wherever possible, and it seems like it would work perfectly well here - instead of creating the abstract class, I think you could just remove the assertion on PaymentProcessor that its authoriser has any particular class type?
    I realise that type checking is often valuable, but in cases such as this where you want something more flexible, why is dependency inversion better than just leaving the type fully open?

  • @Lodinn
    @Lodinn 2 года назад +1

    Great CAPTCHA 10/10 would implement

  • @alexbelov6287
    @alexbelov6287 2 года назад

    Thank you!

  • @nocodenoblunder6672
    @nocodenoblunder6672 2 года назад

    I have a question. Composition is for example if a class Car has an attribute of another class ie Engine and if the outer and inner object have the same lifetime. Otherwise its an aggregate. So if each Car has the same engine by default(engine gets created in car) its not dependency injection only aggregation. If the engine is created outside of Car an is passed through init to assign to Car.engine it is both injection and aggregation. If Car has no attribute engine but uses the engine in one of its method that expects Engine it is only dependency injection. Is what I said correct?

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

    Nice!

  • @fjolublar
    @fjolublar 2 года назад

    Great video but I am a bit confused. You removed the authorizer.generate_sms_code() from the actual code but you used it in the test case. You said you would go back to it but I seem to miss it. Isn’t that very important in the actual code? Where is it going to be run?

    • @fjolublar
      @fjolublar 2 года назад

      in my understanding generate_sms_code() should be called inside the authorize method

  • @rusbelbermudez238
    @rusbelbermudez238 Год назад

    Really nice videos I have learned a lot.

    • @ArjanCodes
      @ArjanCodes  Год назад

      Thanks so much, Rusbel, glad it was helpful!

  • @MrTulptuiz
    @MrTulptuiz 2 года назад

    Man you are awesome!

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Happy you like the videos!

  • @JamesWolfisz
    @JamesWolfisz 2 года назад

    Not sure if your patch question was answered but you can patch __call__ of the class and return whatever you like.

  • @fmictsang8874
    @fmictsang8874 Год назад

    Thanks.

  • @zdzichuWentyl
    @zdzichuWentyl 2 года назад

    as always good work

  • @Korcari1
    @Korcari1 2 года назад

    Thanks!

  • @nestorguemez4846
    @nestorguemez4846 2 года назад

    Excellent video

  • @selimrbd
    @selimrbd 2 года назад

    Is there a video where you explain the difference between a design "pattern" and "principle" ? It wouldn't shock me that dependency injection be called a "principle", same if dependency inversion was a "pattern". Is there a clear-cut difference between both or is it fuzzy ?

    • @ArjanCodes
      @ArjanCodes  2 года назад +2

      The way I view it is that a principle is more of a guideline, whereas a pattern is a specific solution to a design problem. In that sense, "dependency inversion" is a principle because you can apply it in many different ways, whereas the "strategy" is a design pattern, because it prescribes a specific organization of classes and methods. To draw a carpentry analogy: a design principle is "make your wooden construction such that it can support at least twice the weight of what's needed"; a design pattern is a dovetail joint.

  • @simonkufeld7903
    @simonkufeld7903 2 года назад

    good explanation

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

    Dependency inversion is then something like the maxim "couple to interfaces, not implementations".

  • @davidlayton5054
    @davidlayton5054 2 года назад

    Great to see you solve the problem that you created by using the type system. It shows the cost of adding the type, but what is the marginal value (on top of unit tests)? The abstract class reduces the dependency to the interface the ABC defines, but if you'd not used types (and the interface was wrong) your test would just throw an attribute error that makes the issue just as clear. I honestly don't get it. Can you explain?

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Hi David, the Python interpreter completely ignores type hints - they don't have any effect whatsoever on what the code does when you run it. The main reason types are there is to provide help to the developer. Thus, types don't create problems, they make them explicit, and allow using tools like Pylance and Mypy to help you avoid mistakes while you're writing the software.
      I think it's a bad idea to use failing unit tests for establishing what the interface is between pieces of code. Do you really want to sift through a bunch of unit tests to figure out what kind of objects with attributes should be passed to a function as opposed to having that information clearly defined by types (which can then be automatically checked)?
      Finally, if you write code in a team of developers (which is probably true for many developers working on commercial software), types help establish what the interface is, and that + automatic type checking makes adding new code or refactoring existing code faster for the developers in your team.

    • @davidlayton5054
      @davidlayton5054 2 года назад

      @@ArjanCodes thanks for the response. By problem I meant the coupling. After the pendacy is injected, there's no longer any dependacy on the class beyond the annotation.
      I see how something like mypy is useful if you're not writing tests, but I fall to see what it adds if you are. Is there an example of a type of issue MyPy would catch that a good unit test wouldn't?

    • @9e7exkbzvwpf7c
      @9e7exkbzvwpf7c 2 года назад

      @@davidlayton5054 "Is there an example of a type of issue MyPy would catch that a good unit test wouldn't?" Yes, it is true that if you commit to having 100% test coverage static typing won't catch any errors your tests wouldn't. However, why sign up for 100% coverage and the time that entails when you can use static typing while being sure that the case of "are the types correct" is getting caught at build time?

    • @davidlayton5054
      @davidlayton5054 2 года назад

      @@9e7exkbzvwpf7c Thank you so much for your reply. I definitely see the value in what you are saying. I do TDD b/c I program a lot faster that way-- not b/c I think it is the one-true way. So I'm personally already signed up to 100% coverage. I certainly see the value in tool like MyPy.

  • @cs-ope
    @cs-ope 11 месяцев назад

    You're too damn good

  • @MrCucolea
    @MrCucolea 2 года назад +1

    Amazing content! Creating a video about testing and mocking with pytest would be very interesting in my opinion.

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      Glad you like it Robert - and thank you for the suggestion!

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

    so dependency injection is the way to pass a class and inversion is the interface / abstract so the passed class not tied to one specific class. noted, thank you mr arjan

  • @uguree
    @uguree 2 года назад

    Great video,
    I would like to send you a code so that you can refactor it :)
    I used nested classes in a class and one of them inherit from the other one,
    As I used argparser with parameters and as I had to create entrypoint by creating main class, but one of the parameters had to be passed into the nested class so I passed it like super() .__init__(log_file)
    So I messed up, code works well but noone can dare to unittest :)

  • @hakimbhd9736
    @hakimbhd9736 Год назад

    You are the best 🤩

    • @ArjanCodes
      @ArjanCodes  Год назад +1

      Thanks so much Hakim, glad it was helpful!

  • @tokero5199
    @tokero5199 Год назад

    I'm new to python, I'm wondering why use an Abstract class for the Authorizer, why not use an interface?

  • @programminginterviewsprepa7710

    But how do you inject objects deeper in call hierarchy??

  • @maikwiesmueller
    @maikwiesmueller 2 года назад

    When I saw the title i got instantly triggered by the "VS" just to find out that I love this video. So was it clickbait? Maybe, but i love it!

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

    Is dependency inversion just Dependency Injection + Strategy Pattern?

  • @PawelOlas
    @PawelOlas 3 года назад +1

    test_init couples your test to the implementation. if you decide to refactor and rename the variable to _authoriser, the test will break. IMHO you should test the public interface of the class to allow free refactoring of your implementation later. Technically "authoriser" variable is public in this example but maybe it shouldn't?

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Indeed, in a production application I would definitely hide attributes behind properties or methods.

    • @PawelOlas
      @PawelOlas 3 года назад

      @@ArjanCodes I understand that you want to simplify the examples to demonstrate something but at the same time there are people learning from your code and they may think that this is what they need to do. They don't know that you do not do it in production code. I really like your lessons but as a learning resource I think the code should be perfect and production ready.

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

    OOP brings a whole other level to programming. Much more than watching simple C language tutorials. I can't imaging Oracle bringing these OOP concepts to PL/SQL...or GNU Scheme...? Didn't the industry switch to Data Oriented Programming?

  • @JonMartins
    @JonMartins 2 года назад

    Do you think the initializer test is actually needed? Aren't you testing something that belongs to the python codebase? Instances of classes should be of the expected type