True. But what's even worse for your career is when you learn programming languages that don't need DI frameworks because that kind of clean separation is baked in (like Python) but then most jobs out there are Java :-(
I'll admit I've never done Java programming, but (so far as I understand the term) dependency injection is something I use a lot when writing Python code. For example, if I'm writing a method to transfer a file over a particular kind of connection, I'll make the connection object a parameter rather than have the method set up the connection itself. If I'm writing a class, I might make the connection object a parameter to the constructor, or just construct it in a helper method that I can override for testing purposes.
Am I missing some magic goo that makes Python not require DI, or do I just not understand what Java programmers mean by DI?
For languages such as Java and C#, wiring up these objects is quite a pain.
For duck typed languages such as Python and Ruby, it isn't that big of a pain. So we just do it. Just because you don't have an IoC framework doesn't mean you aren't practicing DI :)
Am I missing some magic goo that makes Python not require DI, or do I just not understand what Java programmers mean by DI?
Sometimes it's easier to assume Python-developers are blind to architectural problems and common solutions to those.
Not saying Python isn't used in large, complex problems or that all Python-code is spaghettti-code, but there are a lot of wannabe cool Python-hackers ("ninjas") out there and part of being "cool" in that sense is disregarding everything which looks even slightly like design patterns.
In those circles "Design patterns" means enterprisey, verbose code with FactoryFactories and not guidelines for how to structure your code to solve problems which have been solved before you.
There are a lot of design patterns for object oriented programming that were created specifically to overcome structural flaws in C++ and Java. These simply don't apply to some languages.
Dependency Injection is not one of these and actually has widespread applicability. Although the name always sounds wrong to me since it makes me think of dependency creep, one of the problems that it is intended to alleviate.
Design patterns have gained an "enterprisey" reputation because much of the time they are represented as if each one is a universal, language agnostic solution. They reek of cargo cult management.
Every language and/or ecosystem will have its own set of effective design patterns that will partially overlap with those of other ecosystems. Some communities care more about making names for the patterns, which could in itself be considered enterprisey.
Interestingly enough, the person that first told me about design patterns was a Python hacker.
I am curious about how this type of "clean separation" is generally handled in Python. I mostly have experience with Java, and I have some light Python experience but I've never worked in a large Python codebase.
Would you mind telling me how you would handle this in an example?
Let's say you were building a web app which had to communicate with some sort of payment gateway API. Naturally since you are communicating with some third-party API you want to prevent the rest of your codebase from knowing too much about the API - in case you ever need to change gateways - and so you wrap it up in an interface/module/etc to abstract away the details of Gateway X.
If it ever came time to switch payment gateway backends, in a Java application using DI you would just need to switch which implementation of your interface that the rest of your code is wired up with (either in XML, or if you are wiring up collaborators explicitly in your code, etc).
How would you generally manage this type of thing in a Python system? By giving the web/controller code a different payment module that implemented all of the same method signatures?
you want to prevent the rest of your codebase from knowing too much about the API - in case you ever need to change gateways
Maybe I'm naive, but I wouldn't usually bother:
1) Maybe I will never change gateways, then there was no point in writing a wrapper.
2) If I do change gateways, I can just write a wrapper that uses the old gateway's API as the interface for the new one.
If the new gateway is sufficiently different than the old one that it can't be mapped to the old API, then whatever wrapper I wrote back in the beginning before I needed it wouldn't have been sufficient anyway.
Your strategy sounds like YAGNI to me. I only write separate interfaces when I actually have different components that need to be swappable.
But maybe that's just because I'm using Ruby and I can be lazy like that?
"If it ever came time to switch payment gateway backends, in a Java application using DI you would just need to switch which implementation of your interface that the rest of your code is wired up with (either in XML, or if you are wiring up collaborators explicitly in your code, etc)."
You simply pass in the desired payment processor as a parameter. End of story.
This is part of the reason Java is such a terrible language for actually learning about programming. "Using Dependency Injection" is an absurd way of phrasing "pass in varying parameters as parameters to your function instead of using global variables". What should be the normal way of doing business has instead been elevated to Something Special (that Lesser Languages Can't Do because they don't call it Dependency Injection because they would never think of giving Passing Parameters to Functions a special name!) because Java makes something as easy as breathing and in some cases hard to avoid (try programming Haskell or Erlang with too many global variables) a production.
In Java, you use absurd and unnecessary machinery. In every other language, you just do it. It's hard to even explain how you do it because there's hardly any doing in the it. It's a parameter passed in. It hardly seems worth discussion.
For the sake of this example, let's say that the different gateways have vastly different APIs, messaging mechanisms, etc.
So in the code that you are passing a parameter to, you'd have to have some sort of branch based on the parameter value to choose to call Gateway A versus Gateway B - correct?
Which requires you, when switching gateways, to change the value of the parameter passed along with possibly updating the branch to be aware of what the different Gateway modules you might call would be - correct?
This is basic object orientation: You pass in an object that corresponds to the desired interface. "Facade pattern" if you need something to Google, but this is really basic language-agnostic OO stuff.
Another reason to dislike the Java cruft; it successfully hides the simple things going on behind immense machinery and complicated terminology.