High vs Low-Level Code1:59
They all work together to help us design better applications. Now, before we move on though, it's important to first discuss what I mean by high-level versus low-level code. So when I say high-level, I want you to think of code that isn't concerned with the specific details. However, low-level code, that's going to be the opposite. Low-level code is intimate with the specifics and the details. So what this principle states is that your higher-level code should never have to depend upon that low-level code. Or in other words, one class should never be forced to depend upon a specific implementation.
PasswordReminder Coupling Example3:59
So this backs up that Idea that high-level code depends upon an abstraction, but low-level code too depends upon an abstraction. But the interesting thing, like I noted, is when you start asking which part owns this abstraction, that's where things get really fun. Now let's move on to review the obligatory code example, and we'll use one that's very popular. So imagine that we have our class PasswordReminder, something like this. Now we might have within our constructor some kind of database connection, so we could just call that dbConnection. However, I'm going to type into that, and we'll say that's going to be our MySQL connection.
call that dbConnection. However, I'm going to type into that, and we'll say that's going to be our MySQL connection. Now this is bad in a number of ways, but just stick with me here. Now what I want you to do is pause this video and really think about what the downsides are to this approach. When you're done, press play again. OK, so let's think about this. First we come to the question, why should PasswordReminder have any interest whatsoever in what our database connection is? Fine, we're using MySQL, but why does PasswordReminder care about that?
Now how come? Well, once again, high-level modules, which is the PasswordReminder class in this case, should not depend upon low-level modules, which is the MySQL connection. Now what are we supposed to do instead? Well, high-level modules depend upon abstractions, not concrete implementations. And if that sounds confusing, don't forget to bring all of this back to the idea of knowledge. Does PasswordReminder need to have knowledge about how you connect to your database? No, it just needs to know that there is some database connection that it can work with. So instead, we would code to an interface. Once again, we refer to that phrase.
Introduce Connection Interface6:07
So instead, we would code to an interface. Once again, we refer to that phrase. I've told you in every single lesson in this series that code to an interface is laced throughout all of these principles. So if we set up our interface, maybe we'd have an interface called ConnectionInterface, and all it provides, at least in this example, is something that says connect. Now if we follow this principle, PasswordReminder should depend upon an abstraction. Now let's go to that second part of the rule. High-level modules, too, should depend upon abstractions. So now if we create our first implementation, we'll say MySQL or DB, it doesn't matter.
High-level modules, too, should depend upon abstractions. So now if we create our first implementation, we'll say MySQL or DB, it doesn't matter. Let's call it DBConnectionImplementsConnectionInterface. Now once again, we add the method subs, we do whatever is necessary to connect to our MySQL database, and now we are in conformance. The low-level module depends upon the abstraction, and the high-level module depends upon the abstraction. That's how that works. So the main thing I was trying to convey here is that dependency injection does not equal dependency inversion.
Abstraction Ownership and IoC7:45
Now there's just one more thing I want to talk to you about. Remember earlier in the video when I started talking about ownership of abstractions? This can be confusing, but try to think about it. Who owns ConnectionInterface? Does DBConnection own it, so to speak? Or does PasswordReminder own it? Now what I mean by that is, yes, PasswordReminder depends upon ConnectionInterface. However, what if we wanted to update DBConnection? Maybe we need to change the way it works. Well, it's possible that changing DBConnection would cascade to a change in ConnectionInterface,
Maybe we need to change the way it works. Well, it's possible that changing DB connection would cascade to a change in ConnectionInterface, and then once again, that can cascade down to any class that depends upon it. And so as a result, this is what brings us to terms like IOC, Inversion of Control. Or who exactly is in control here? Going back to that electrical devices and the outlet example, who's in control there? Does the device dictate the interface, meaning does the Nintendo or the TV dictate how it fetches power? No, it's the other way. No matter what the inner workings are of those devices, at the end of the day, they
Dependency Inversion Summary9:24
It's a pretty complex idea that could have a drastic effect on how you design your applications. Ultimately, though, the core idea behind this principle is that your high-level code, in this case PasswordReminder, should never ever depend upon the low-level code. It depends upon an abstraction, and now the low-level code depends upon that same abstraction as well. This allows for the complete decoupling of our code while still providing some way for the two modules to interface.
