Now, one more principle. And this one is the one we're going to spend the rest of our time on. And this is the dependency inversion principle. Let's get it right there. That's that, that's that, that's, there we go. The dependency inversion principle. That's that. That's that. That's there we go. The dependency inversion principle. Of all the principles that we've talked about so far, some of them have been moral, you know, like the open closed principle. That's kind of a moralistic principle. Modules should be open for extension but closed for modification. And then you have other principles like the interface segregation principle, which seem to be limited in their scope to certain kinds of problems, like the problems of very large classes or the problems of many incoming dependencies. The dependency inversion principle is a little bit different. The dependency inversion principle is the kind of mechanism behind all the others. So what does this principle tell us? All the other principles use this principle as their kind of low-level implementation. So what does this principle tell us? By the way, almost all the design patterns in the design patterns book use this principle as their low-level implementation. The dependency inversion principle says depend in the direction of abstraction. High-level policy should not depend on details. Details should depend on high-level policy through an abstraction. Depend in the direction of abstraction. Depend in the direction of high-level policy. Now, I've already gone through the argument for this, at least in some levels. I've talked about the copy program and how it's always a good idea to have the high level policy, depending on an abstraction and the low level details also depending on abstractions. And that's an implementation of the dependency inversion principle. You can see the inverted dependencies right here. I also talked about how programs prior to the 1970s were all violating this principle. They all violated the dependency inversion principle because they all pointed their source code dependencies downwards towards more and more detail. And we would like at least some of these dependencies turned around so that they were depending on high-level policies. Details should depend on high-level policies. High-level policies should not depend on details. And the way you do that, of course, is with that interface, which I talked about earlier, right? You insert an interface between the high-level policy and the low-level detail, and that allows you to turn the source code dependency around from the low-level detail towards the interface. And this is power, more power than software developers ever had, right? From the 1980s on, we started to realize that we had this power. Prior to that, when we called functions, we just called them and we used them and our dependencies pointed in the wrong direction. We didn't even think about the direction of dependencies back then. But when object-oriented programming came along, the ability to take those source code dependencies and turn them around was immensely powerful. And we suddenly realized what we had at our disposal. We could turn around any source code dependency that we wanted. We could look at that diagram and we could turn the dependencies that caused us trouble. Now, what does that mean? Well, let me get a better diagram up here. Let me show you what it means. Let's assume that I have an application, and that my application uses shapes, and I have described my shapes using an interface here. And you see that hard black arrow from the application to the shape interface? That's telling us that everything the application wants to do with shapes, it can do through that interface. This is the open-closed principle. The open-closed principle allows us to make changes to the behavior by implementing derivatives, by implementing subclasses. We don't have to change the app. We can simply add new features by adding new derivatives. We could add a triangle. We could add a pentagon. We could add any kind of new shape without changing the application. That's the open-close principle. And we can see the dependency inversion principle at work here. All of the arrows, all the black arrows, are pointing towards the abstraction of shape. But then I've got this red arrow here. And the red arrow is all the new keywords. Somebody had asked that question earlier on. Well, what about new keywords? What do you do about new keywords? Well, okay. Here's the problem. The application must be able to create the instances of the square and the circle and the rectangle and any other shape that comes along. And that means the application has source code dependencies on the derived classes. That's a problem. Why is it a problem? Well, it's a problem because we would like to draw a line right across here. See where my mouse is moving? I want to draw a line right across there, and I want to say that because of the open-close principle, I want to be able to make changes below that line that don't affect anything above that line. Now, I can do that this way. There's the line. See that red line? That red line is the architectural boundary. Above that red line, I want the code to be invulnerable. Below the red line, I want to be able to change the code without making any changes above the red line. That's the open-close principle again. And how did I do that? Well, I used the abstract factory design pattern. I hope that you all have the design patterns book. It was written in 1995, and yes, I know that means it's old, but it is still extremely relevant and very valuable. And pay no attention to the silly memes out there that tell you that the design patterns are archaic and old and they don't matter anymore. That's complete nonsense. I don't know why people do things like that, but they do. Okay, so complete nonsense. Get a hold of the Design Patterns book. Read the Design Patterns book. It's probably one of the most important books written within the last 30 years. In any case, this is one of those patterns. And this pattern works as follows. The application needs to be able to create the square, the circle, and the rectangle, but it does not want to depend upon them with source code dependencies. So instead, it depends upon an abstraction. That's the dependency inversion principle. We're going to depend upon this abstraction named shape factory. The shape factory has three methods in it. Notice one method for each of the derivatives, square, circle, and rectangle. I sometimes call this a 90-degree rotation. There's a 90-degree rotation from the derivatives to the methods in the shape factory. But never mind that. That's just me. Below the red line, the shape factory is implemented such that the make square function will create a square and return it, but it will return it as a shape. And the make circle function will create a circle and return it as a shape. And the makeCircle function will create a circle and return it as a shape. And the makeRectangle function will create a rectangle and return it as a shape. And this solves the problem. The code above the red line is now isolated from the code below the red line. line is now isolated from the code below the red line. Notice that every dependency crossing the red line points toward the higher level side. This is called the dependency rule of architecture. Across an architectural boundary, all dependencies point towards the higher level side. All source code dependencies point towards the higher level side. If you think about this carefully, you will realize that the factory implementation, the square, the circle, and the rectangle are now a plug-in to the application. You could unplug them and plug in a completely different implementation without the application knowing the difference, right? This is a plug-in now. And one of our goals in using the dependency inversion principle is to create a plug-in architecture. Now, some of you have already begun to realize that there's a problem here. And the problem is that if we add a triangle to the derived classes below the red line, we will have to add a new make function to the shapefactory interface above the red line. And that would violate the open-closed principle. So suddenly we have a vulnerability. We are protected from the square, circle, and rectangle. But if we add a new type, we have to modify something above the red line. What can we do about this? We certainly don't want to add the makeTriangle method above the red line. When we add the triangle class, we do not want to add that makeTriangle method above the red line. We need a different solution. And that different solution would be to break that 90 degree rotation. We do not want every subclass represented in the shape factory implementation or in the shape factory interface. What we'd like to have instead is a single method, the make method. And it's going to have to take some kind of an argument, some kind of argument that specifies square, circle, and rectangle. And then down here in shape factory implementation, we can simply have a switch statement or an if-else statement that creates the appropriate kind of object. if else statement that creates the appropriate kind of object. What should the argument of this make function be? Well, you might think, oh, it ought to be an enum, but it can't be an enum. And the reason it cannot be an enum is because the enum would have to be declared above the red line. And that's the place we don't want changes to take place. We're going to have to make it something opaque, like a string. A string would work, or a raw integer would work, as long as you didn't define any constant names, because the constant names would have to be defined, declared, and defined above the red line. A string would work nicely, though. We could pass a string in, and that would work great. Now, if we add a triangle, nothing above the red line changes. Everything that changes is below the red line. We have the open-closed principle back. Yay! But don't you hate the fact that this is a string now? Don't you hate the fact that this is a string now? Don't you hate the fact that it's a string? Why do you hate the fact that it's a string? You hate the fact that it's a string because we have violated type safety. For 30 years, we have been dominated by statically typed languages. C++ was statically typed. Pascal was statically typed. Java was statically typed. C Sharp is statically typed. Now we've got a whole new batch of statically typed languages like Kotlin and Go and a whole bunch of them, right? For 30 years, we have been dominated by statically typed language that enforce type safety at compile time. Why have we been dominated by these languages? We've been dominated by them because of a war that was fought in the late 90s. That war was fought between IBM and Sun Microsystems. Sun Microsystems won that war. And the war was for control of the internet. Which of these companies is going to be the dominant market force in the internet? And both of them decided on the same strategy. The way to dominate the internet in the mid-90s, was to provide the language that software developers would use to code applications. Sun Microsystems chose Java, which was a derivative of C++. IBM chose Smalltalk, a dynamically typed language. chose Smalltalk, a dynamically typed language. Why did they choose Smalltalk? Well, it turns out that a very well-known consultant by the name of Capers Jones had done a research study on how productive programmers were and found that Smalltalk programmers were five times more productive than C++ programmers. That was the nature of the war. IBM came out and said, we can make your programmers five times more productive. And then all the C++ programmers stood up in proxy for the Java programmers and said, yes, but you're not safe. You're not safe. And the small talk people said, well, we write tests, so we're safe. And then the C++ programmers said, to hell with your tests. We need the compiler to check. And the C++ programmers won that argument. And Sun Microsystems became the dominant force in the internet. And IBM closed down their small talk offerings. And that was the reason, that is the reason we have been dominated by statically typed languages ever since. But here's the case, right here. You must, at this point, abandon type safety. Not everywhere, but across that boundary, at that one place, at that shape factory, you must abandon type safety. There's no way to satisfy the open-close principle without abandoning type safety at some place across that line. Static type safety, not dynamic type safety, static type safety must be violated across that red line. The small dog programmers kind of slunk away, and they all became Java programmers, but they took their testing discipline with them. Nowadays, that's called test-driven development. The Java programmers started writing tests, and that infected a whole bunch of other Java programmers. And this testing discipline spread like a kind of virus through the Java community until a few years later, all these Java programmers were writing tests and wondering why they were using this language that was slowing them down so much. They all decided to go five times faster and they all became Ruby programmers and Python programmers. And that's why the Ruby Python fad began in like 2005 until just recently. And then, of course, all the people stopped writing tests, a whole bunch of people stopped writing tests, but continued to use dynamically typed languages. And then they got in trouble because they weren't statically typed. And so now we've got this big pendulum swing back to statically typed languages. And that, by the way, that pendulum has swung many, many times. Static typing, dynamic typing, back and forth, back and forth. I've seen it swing probably at least three or four times. Okay. Those are the solid principles. Let's see if there's any questions that we can answer. And let's see, one of them is, can are the solid principles. Let's see if there's any questions that we can answer. And let's see, one of them is, can we apply solid principles in languages like Clojure? Yes, I do that every day. And I've just finished a book all about that, which is called Functional Design. That's the working title anyway. Should be out sometime in the fall. But yes, all of these principles can be applied in any language at all. It helps if it's an object-oriented language, or at least it helps if it's got some kind of polymorphic behavior, because doing it in COBOL or FORTRAN is really tricky. Can we say that some classes of Java break the Liskov substitution principle. For example, the implementation of empty list, a list that throws an exception on the add method. Yeah, that is a clear Liskov violation. And there's also a number of others. There's immutable list, which is a derivative of list. Again, that violates the open close principle. If you know you are using it and if you are avoiding the if statements that would follow from using it, then you're probably okay. But yeah, in general, those classes take features away. And any kind of subclass that takes features away from a base class violates the open-close principle and leaves you open to an open-close principle violation. Wesley says, should we create an interface for everything or only when we see that the code can change in the future? I don't create interfaces unless I need them. Now, when do I need them? Well, I need them if I have a change requested by the customer that I need to isolate myself from. Then I'm going to create an interface. When else do I need an interface? When I'm writing a test and that test wants to test some high-level chunk of code without invoking low-level code. And I will put an interface in between them and stub out the low-level code. So sometimes writing tests forces you to write, to create interfaces. Sometimes changes to the system made by users will force you to create interfaces. And sometimes you just look at the code and go, I should put an interface in here. But I want to make sure that you don't follow this funny rule that says you should always put an interface in because you should not always put an interface in. Put them in where you need them. Another question here about the interface segregation principle, the job interface needs to keep existing, or can we remove it and only use the segregates? So the idea there was that if we segregate the job class implementations, we wouldn't need that last job class that multiply inherited everything. And that's true if you can segregate the implementations. But very often you cannot do that for one reason or another. There's some reason why the functions in the job class need all of the variables. And so there's no good way to separate the implementations. The implementations themselves are coupled, but we don't want that coupling to be seen by the clients. Let's see, what do you think about containers for dependency injection? I think they're fine. I don't mind dependency injection, as long as the artifacts of dependency injection. I think they're fine. I don't mind dependency injection as long as the artifacts of dependency injection do not make it past the lowest levels of your code. I do not want any funny little auto-wired stuff or any of the trappings of dependency injection to make it into your business rules or your business objects. The dependency injection framework should be kept at the outside of the system, and you should protect the inside of the system against it. What that means is that you're going to inject dependencies at the periphery of your system and then pass things around inside your system by normal means. And I think that answers all the questions. So, and that, well, we're about two minutes over. So, thank you all for your attention. Thank you for being here for two hours. I had a lot of fun doing this presentation. I hope you all learned something, got something out of it. And I will turn things back over to Wesley to close things down. Very, very good. I learned a lot and a lot of details that I thought that I knew. And definitely every time when I see some content from Uncle Bob for me it's a kind of mind blowing. So Uncle Bob, thank you, thank you very much for your time here with us. People from the NBA are going to love it. So thank you very much again and I hope that we can see you soon and are going to love it. So thank you very much again. And I hope that we can see you soon. And I'm going to put on the links in the description your books and your mentoring program. And also let me know when you publish your book about the Clojure. It's going to be very fun to understand how to work with solid and functional programming. So it's going to be nice. So thank you very much, Bob and see you soon. All right. Bye-bye.