 I'm here today to tell you a story, and it might be a story you know. You love your life, and you love Ruby, and you love Rails, and you wrote an amazing app, and everyone thinks you're brilliant. And then the customer asks for a little change. So you turn it off, and you open up your text editor, and you grab all the files, and you make one little change, and then you make another, and then you make a few more. And then you get all the tests running again, and check it all in, and you throw it proudly into production. And then they tell you they want to change their mind. They didn't really want this thing, they wanted that thing instead. So you open up your text editor, and you grab the files, and this time, you make a bunch of changes, and then you make a whole bunch more. And frankly, this is not as much fun as you thought it would be, but you get everything running, and you check it all back in, and you deploy it to production. And then they ask for a new feature. And now it's a crisis, and everyone's hair's on fire, and it just was not designed to do this. And you just do whatever it takes to work. You hack away with it in a machete until you get it running, and then you throw it back into production, and you deploy it. And you start wondering if maybe there's a way to check code in without putting your name on it. You realize that the app is a wreck, and that you hate Rails, and you hate Ruby, and you hate your life. We all know that story. In the beginning, the app was a delight. You felt great. You were getting so much done. And that feeling of deep satisfaction is because of how efficient and effective and useful Rails feel. Something just happened, okay, sorry. So we feel happy when we're doing our best work, and those feelings stand as proxy for the state of your app. Unfortunately for many apps, this feeling peaks right about the time when you release the first beta, and then it goes downhill or after. Things get worse and worse. You were fast, now you're slow. You were happy, now you're sad. You were cost effective, and now you're a money pit. The app was amazing, and now it's just a mess. This does not mean that it's time to look for a new job. Amnesia won't help. Unless you change something, every app you write, you'll end up hating. These applications are prisoners of their designs, and they've taken you hostage. You can't outrun this problem. You have to do something different. You have to learn to understand the mess. And the problem with messes is that they're messy. They're hard to understand. Everything is tangled up together. They're like big tapestries made up of many different threads where the warp and the weft are woven over and under, and you can't pull on any thread without dragging something from a very different, dragging something along from a different part of the tapestry. In the beginning, everything's idyllic, but as soon as you start making changes, that illusion gets shattered. Your app isn't a pastoral scene with cute furry little amps. No. Your app is trying to kill you. Things are so woven together that you can't pull on any single thread without breaking many things. And this coupling, this mess, is because of knowledge. Knowledge. Your app is made of objects, and objects know things. They always know things about themselves, and sometimes they know things about others, and it's knowledge of others that weaves objects together. When an object knows something outside itself, that's a dependency, and we all know about dependencies. When something you depend on changes, you might be forced to change in turn. You cannot avoid dependencies. Since objects have to collaborate, they always know things about one another, but there are many ways to arrange code to solve any problem, and you can easily arrange dependencies so that things turn out badly. Controlling dependencies requires understanding the stability of the various bits of knowledge in your app. Am I gone? Can you say, okay, good. Sorry, we're having a little AV trouble. Let me get further away from that. Okay. Here we go, stability. So everything any object knows can be ranked along a continuum from completely stable to completely unstable. And it doesn't matter. It doesn't actually matter how stable something is. It just needs to be more stable than the things that depend upon it. Stability is relative, and things that are wildly unstable in some circumstances, from some points of view, might be very stable from others. It's also true that sometimes you just don't know. You're confused. You're confused about why your parents put you in this snowbank. I flicker is amazing. You're confused about how stable a bit of knowledge is. What are you doing, honey? Oh, you're heckling me. Here, it's for you. I love this drill. Go sit down, honey. Stability. Sometimes you don't know if things are stable or not. You don't know if you can depend on them or not. So this is where object-oriented design gives us hope. Object-oriented design is the ultimate dispenser of programming treats. Hope. Oops, sorry, hope. Design lets you write code that your future self will love. Even in the face of confusion and instability, it knows how to separate the stable from the unstable, and it shows you how to depend on the first and hide the second. It gives you a way to manage the mess so that you can write apps that are efficient and fun forever. It changes everything. Object-oriented design lets you stop worrying and learn to love the mess. And I'll show you how. It's easy. There's three examples. The first example is gonna just set up the problem. The second one is gonna make a complicated code. And in the third example, a miracle occurs. So the arc of this talk and the arc of the examples are this basic premise. Concrete code is easy to understand, costly to change. Easy to understand and costly to change. Abstract code is harder to understand when you first look at it, but it's much cheaper to change. Design, the principles of object-oriented design, move your code toward more abstraction. So it's not that there aren't costs involved, it's that the benefits outweigh the costs. And you'll see that in the examples or you'll see that in the arc of this talk. So, example number one. Something bad just happened here. I can fix it. Example number one. Label the knowledge. So imagine, here's a game where people race bicycles. Players have to buy parts and one of the parts they can buy is a shock. Before buying that shock, a player wants to know what a shock's gonna cost. So here's the code, it's already written. Players send shock costs to game, who gets a new instance of shock and sends it the cost message. This method is a big, ugly, complicated mess. It's pretty much incomprehensible. And the customer told us they weren't really sure it was right, that they know it's gonna change but they need more information. Sometime needs to pass till they figure out how it should work. Even though you can't see the code, assume it has these qualities. Nothing inside this method can change that will force a change out in the app. And nothing out in the app can change that will force a change inside this code. So this mess, because of those qualities, this is a special kind of mess. And I'm gonna give it a name. I'm gonna call it an amegamess. So amegamesses are at the end of the line and they have no dependents and no dependencies. And so you can think of every kind of mess in your app is falling into one of two categories. It's either an amegamess or it's not. So the question here is what should we do? Should we do something about that code? It's heinous, it's really embarrassing. You would not want your friends to see it. So what happens, what will happen? What will the consequence of this code arrangement be if you walk away right now? So before, let's postpone that question for a minute. Hold on to it, we'll get back. Let's look at a picture first. This is a picture of objects and the messages as they pass between them. It's a sequence diagram and I love them. It's also UML, but trust me, it'll be okay. Here's how sequence diagrams work. The boxes represent objects. The objects can be an instance of a class or a class itself, they're just objects, we don't care. They repeat at the top and the bottom and this vertical dashed line connects the two same boxes. The horizontal solid line is a message that's getting sent. The filled in dark arrows on the end of the receiver. This vertical gray bar tells you game is busy in the shot cost method and the result goes back on a dashed line and again the filled in arrow is on the side of the receiver. I drew all these pictures with web sequence diagrams. I took their name off the rest of the slides but there's a credit at the end. I'm gonna draw some pictures on the slide. I think you can probably see them. So I'm gonna put arrows that are gonna represent external dependencies and then dashed lines with zeros at the end that are gonna represent internal knowledge and then I'm gonna color code it for stability. It's a stop light so very unstable things are red down to really stable things are green. So what this diagram shows is that game depends on the name of the shot class. It depends on the fact that the shot class knows the new message. It depends on the fact that it knows about the cost message and it depends on knowing who the receiver of that cost message is. Shock knows that it implements cost and it knows how it does so. So you can see from the drawings on this diagram that shock has no external dependencies. It's at the end of the line it depends on nothing else. Game depends on a surprising number of things about shock. This sequence diagram is a much truer representation of the relative importance of thing than is the code. The big mess which dominates in the code it might be a hundred lines long. Here is represented by this little gray bar. From outside of shock all you need to know is that you can invoke it by sending this message. And the mess is therefore hidden behind the message. This view of reality is deceiving because it feels like number of pounds of code ought to relate somehow to importance. And it might be if we wrote perfect code that might be true but it's very often not true. This implementation of cost is like a big loud, big boisterous guy at a party who's like dominating and he's in a room surrounded by introverts who can't get a word in edge wise. When you walk into the room he's so loud it's easy to think that he's the most important thing but the stuff here that needs to be listened to is the introverts, there's a lot more information. Do not be misled, it's not about the code. It's not about those classes that are filled with characters that you typed into that file on disk. It's about the living, breathing, running, application in memory, it's about the message. Okay so now that you've seen a bit of code let's step up to 10,000 feet and sort of diagram that. We're gonna plot it, diagram it like it was a sentence. So this is a plot. On the horizontal axis we have stability. So really stable things go over on your left. Very unstable things will be on this side. The vertical axis is about what the object knows whether the information that the object has is within its purpose or outside of its purpose. You can think of that as being whether or not it's the object's job or whether or not it's within its responsibility. Knowing which quadrant a bit of knowledge falls into tells you how to behave. The top left quadrant stable things that are within your purpose are part of the public API. Unstable things that are within your purpose are private behavior and they should be hidden behind the public API. Anything that's below the line is a dependency. You should expose your public API. You should hide private behavior behind that. Stable dependencies that are outside of your purpose you have to have in order to collaborate with other objects but you should minimize them. And here's the key. Unstable dependencies that don't belong to you should be moved. If knowledge falls into this quadrant something about your code needs to change. So on the sequence diagram we saw that shock knew these two things. It knew the name of the cost method and the internal calculation. Game also depended on the message, the cost message, the name of the shock class and the cost receiver. We're gonna dump game for now and just talk about shock. So number one, the cost method is about the most stable thing shock knows. It's part of the public API. It goes high up in the left hand corner of the plot. Other objects can safely depend upon this piece of information. Its internal implementation is very unstable. We know it to be but it does not matter because it's completely within shock's purpose. So this is something that we're allowed to know. Shock is allowed to know this but it ought to hide it behind that message. So the plot tells us to do these two things with that bit of knowledge which is actually already true about the shock class. And so here you go, example one. About the simplest bit of code on earth and I've been talking about it for 10 minutes. So early I asked you what are the consequences of walking away? We all know that the cost method is ugly and embarrassing and we all know that it's gonna change. At this point in design, design asks only one question about this code. It wants to know what is the future cost of doing nothing now? You will never know less than you know right now. It may, if it does not cost you money to do so, you should wait. The most cost effective design strategy is very often to do nothing. This might be such a case. The cost method, since the cost method isn't a mega mess, since it has no dependence and no dependencies, there is no way that it can affect it. Changes can affect your app or any changes in your app can affect it. It is not connected in one of the threads on your tapestry. Having said that, having said that it's okay to leave it if this code were mine and I had 10 minutes I would make a few changes. I would inject that dependency. Makes my testing a lot easier. I would refactor this code. I would make it a bunch of little tiny atomic methods with intention revealing names. You might not. That's a judgment call. I do these things almost without exception because doing a refactoring that simplifies code is like buying a ticket in a lottery that's rigged in my favor. It pays off so often that my best strategy is to play every chance I get. I don't think of that refactoring as design. It's just housekeeping. It's cleanup. Right? It's the last scan through a piece of email to make sure you don't have typos. So for now we're just gonna leave the code. Let's just leave the code just like it is. And so we're done with example one. So let's make it more interesting. Let's change things. The game's a big success and now they want more shocks. And each shock is gonna have a different cost. There's a different cost calculation with every shock. It's really nice to be in Colorado so I don't have to explain what a lefty shock is. But other people were very confused by that in the example. So a well-intentioned programmer has gone in and made the change and they added type to the game. The player picks a type, it gets passed into the shock cost method and then passed on to shock and it's initializer. The changes to shock, it's obviously been changed to remember that type. But it now has this big case statement that has four different cost calculations. Here's the sequence diagram for this code. Now I told you before that sequence diagrams were a better representation of what was going on in code than code itself. However, they're not perfect and there are coding sends you can commit that escape notice in sequence diagrams. If you notice here like the types, I love, look I have them, I love that. The types are being passed in and I put a comment here around the conditional methods but really the cost is still represented by just a little gray bar at the end of the line here. I told you before that omega messes have neither dependence nor dependencies and the question about the change that we made in this code, is it still an omega mess? Here was a list of things that shock new. It used to know just one cost calculation and now it knows four and in addition to that, it knows the symbols for four shock cost types and it knows the mappings between the two. These things are known to but not owned by shock. What will happen if the application adds another shock? What will happen if any one of the cost calculation changes? Suddenly there are many ways in which the outside app can change that will force you to change things inside the cost method. The case statement caused an explosion of dependencies. It's not an omega mess anymore. It's something else entirely. It's what I'm gonna call meta knowledge. And so meta knowledge is knowledge about knowledge. The omega messes should be hidden inside the pieces of that puzzle but meta knowledge is about how the puzzle is put together. And object-oriented design, and it's actually, sorry, it's the domain of design. Damn it. Shoot me now. Thank you. It's the domain of design. So let's move back up. We're gonna use object-oriented design to rearrange the meta knowledge to remove those dependencies. We don't need to talk about the shock cost method. Shock should still, on that it's still in the upper left-hand corner of the plot. So let's first look at number two here, the four shock cost calculations. When we had one, it belonged, it was within shock's purpose. But now that there are four calculations, it's incredibly unstable and it probably should not be known by shock. This is not shock's job. What about the fact that there are four different types of shock? It's the same deal. This is incredibly unstable. You know it's gonna change. It doesn't belong in this class. This is a dependency where some outside change is gonna force a change in this method. The mappings between what type gets what calculation, well, that's probably more stable but it probably doesn't belong here. So these new dependencies are what makes code costly to change. This method used to be independent and now it's woven into the rest of the app. It's not only costly to change, it's likely to change. Where you have fourness, you're somebody gonna have threeness or fiveness. Adding that case statement ruined a perfectly good omega mess. And it's really unfortunate because object-oriented design, there are many, many techniques that will let you avoid this and they're simple. It just takes a little bit of knowledge of OOD. And here's the guiding principle. I'd rather send a message than implement behavior. I don't wanna know things. I just wanna send a message to some other object. Well that's great, but it's really clear that what happened here is there was no one to send a message to. That's how this happened. Shock is at the end of the line and it already knew how to calculate one cost. So when we asked the question, where should I put this new code? The cost method seemed like the obvious place. But that's the wrong question. That is not the question that object-oriented design asks. We got in this mess by asking, where should I put this code? And we can get out of the mess by asking, what message should I send? And then we'll just make up an object if we have to and we'll send that message. So let's look at that. Composition, object-oriented composition. I would explain it to you but it is so simple, I'm just gonna show you. We got this ugly code, ugly dependent laden code and we would rather send a message than implement behavior. We don't wanna know things. We just wanna ask somebody. So there's a repeating pattern here. So let's look at just the first one. I don't wanna know this calculation. I just wanna send a message. So what message should I send? Well how about compute? Well I compute, that's a good message. So where should I send it? Well I don't know, let's just make something up. I'll make up a new class. Name it after the type, front shock cost. I'll move the mess down there into its compute method and now it's hidden. This returns that mess to an omega mess. I can safely hide it. This stuff is hidden, completely hidden behind the compute message. And so now that we have somebody to ask, I'll just send the message. I'll change the original code to get an instance of that class and send it compute. You can just do it, repeat, rinse and repeat. You can hide all the messages by making a new class for everyone. And then you can use the classes and send the message. So here's where we are. Now I promised you a miracle, but this doesn't really seem like one, does it? It's hard for people to see this whole refactoring because they imagine going here. They imagine ending here. And this does not feel like an improvement. As a matter of fact, so now the case statement still has a ton of dependencies and I made up four new tiny classes. It's easy to ask the question, is this a real improvement or did I just complicate things in the name of design? Well, what did this accomplish? I used to have four shock cost calculations and now I got four classes. And I used to have the mapping between four types and four calculations and now I have the mapping between four types and four classes. There is an improvement here. Every calculation has been removed from that case statement and put into a class of the design. They now change independently. You can edit those files without breaking other code in your class, in your app. However, there are still dependencies here and the dependencies are in this case statement. If you add or remove a shock, you still have to change code here. And I hate that, so let's just, let's fix it. Notice the pattern. Tight name, class name. This is like saying boo-foo. And so because we control these names, we can do a very Rails-like thing and create a convention. And our convention, it's easy to create a string that holds the name, the string of the class we want. And if we can construct that string, we can use a tiny bit of metaprogramming and turn it into a class. Now I can hear all the really good Ruby programmers in here freaking out because of the eval. Like you probably shouldn't do that. Yeah, there you go. Thank you, Wayne. Okay, so this is like a JavaScript eval, right? Like it turns it into the object. So to make Wayne happy, don't do that. You can do this instead, right? Ruby has this list of constants where you can index in by the string name and get the class back. So saying object cons get class name is exactly like saying foo-shock-cost. But if you're a Rails programmer, which you probably are, because you're clearly using titleize, you don't have to do that. You can do this instead. You can just send constantize to any string and you get the class back. That's an object. And we're object-oriented programmers. This step, this transition where you make up a class name and turn it into the object that is in the class is something that it's really useful to get comfortable with. So we manufactured an object. And I use that word deliberately because this is a factory. Now I know some of you break into a cold sweat when you hear that word, because you got PTSD, because you came from one of those languages where everything's hard. But you're in Ruby now and the pain is over. We love factories. Not only do we love factories, but we like this word. I want this word. Go forth and claim it. This is a perfectly good word. It means exactly what we want. An object that creates some other object is a factory. That's all it means. It doesn't mean it's hard. It doesn't mean you're gonna hate yourself. It just means that you use some bit of code to manufacture an object that you can use. It's as simple as that. So now that I have that factory, I can remove the entire case statement. I no longer need to know the class names. I don't need to know the shock types and I don't need to know the mappings between them. I just need to know the convention. I can write code. I can change this code that breaks every time you change something in your app for this code, where if I want to add or remove a shock type, I just create or delete a class. This is a huge win. Now, one caveat, I probably wouldn't leave it like this. I would extract the factory into some other class, but there was no point in going. You get it, right? There's no point in me showing you that next example. If you can get, like the problem very often with object-oriented design is that you need, you want an object to which you can send a message, but you don't have that object. And factories are the solution to that problem. So that's object-oriented composition. Here's what it looks like. We had the shock class. It had a method cost that did a calculation that we jammed four different calculations into. We fixed this problem by picking out a message. We picked out a message. We extracted each of the calculations into a class of its own. We, then we took the, we defined the message and took the API and we had shock send it and every one of those guys implemented. Once we did that, once you arrange code that way, you can add any new shock coster just by having an implement the interface. If you create a new class, all you have to do is make it define that message. The message defines the API of the duct type, and it's the API of the role. Shock's cost method doesn't know or care about the class of the receiving object. It expects every collaborator to implement this role. Think of collaborators as a kind of its role rather than a kind of its class. When you do that, it changes everything. You can manufacture the right kind of thing, trust it to play the role that you expect. When you do that, you can just send the message and everything gets easier. The dependencies disappear and your app becomes a box of pluggable parts that are interchangeable like Legos. Design happens from the message sender's point of view. We don't send messages because we have objects. We have objects because we send messages. Design is not in the business of dictating perfection. It knows there is no such thing. It's only, it only requests that you remove dependencies in the dependence from your message and hide them behind stable interfaces. It allows you to recognize metanolids when you see it and it tells you how to use the principles of design to rearrange code in order to remove dependencies. So there you go, object-oriented design, it's just a set of tools. And if you have the tools in your toolbox, you can pull them out and use them when the situation calls for it. And the truth is, even if you don't study, even if you don't make an effort to learn object-oriented design, if you do enough object-oriented programming, it will teach you these rules. For example, there was a time when I thought that I had invented the law of Demeter. I swear. I did, I was writing code in a cave and I wrote bad tangled messed up code for long enough so that I finally noticed that pattern was a bad one and that if I avoided it, my code got better. I paid the price. They say that good judgment comes from experience and unfortunately experience very often comes from bad judgment and I have plenty of experience. I walked into the headwind for a long time, not realizing that there was an easier way. You might be special, but no matter how special you are, there's no need to suffer. We can learn from those who have gone before. Object-oriented design says that stability is relative and you should notice it and depend on things that are more stable than you are. It says that what you want is always more stable than what they do. What they do is messy and uncertain and changeable, but from the outside, from where I stand, it does not matter what's inside that house. If you arrange code so that you cannot see the mess, it does not exist. We stand on the shoulders of giants. We truly do. We are lucky to live in an age where the best thoughts of anyone are available to everyone. I'm not saying that we're all Isaac Newton, but you gotta start somewhere. We begin as infants of design, but you can grow and learn and we can teach each other. It isn't always easy. There's no guarantee it'll be fun every step of the way, but if you persevere, a time will come when your applications bring you nothing but joy. You will never outgrow the principles of design. If you learn them, you can use them and make them your own, and you can write applications that you'll love forever. Thank you. Is it time? Time for questions? Yeah, we can have a few questions. A few questions. Okay, see, he knew the secret. Come on. Dan, what's your question? Yeah, Dan, come on. My question is, how come you don't use a type class instead of a cost class where you'd have leftshock.cost? There are so many ways to solve this that that is a completely reasonable solution, right? Yeah, so you could do this with inheritance. It depends on what the shock looks like. What else is in shock, which we don't have a way to show here, but yeah, you could. I don't care how you do it. Like all I want is to do whatever saves the most money. Yeah, that's good. Right? So in your situation, that might be the perfect solution. And I'm sorry, I gave him the, he had a preview of the fact that I was gonna. I would have asked this question anyway. I have one copy of the book. He just got it. Yes. All right, next, do you have any questions? Anyone else? It's right, it's right now. You can get it on Amazon, if you buy it through my link, you can give me one. Yes, can you yell it out? We'll repeat it, yeah. Yes, absolutely, yeah. My goal is to remove the dependencies. I'm gonna write code that can't be affected. As soon as I get here, oh, sorry, I'm sorry. She asked if the goal was to isolate those things as omega messes, right? To push each branch of the case statement out. So this is a classic object-oriented technique. It actually has a name called Replace Conditionals with Polymorphism. That's a thrilling name. I know you would have loved that talk. So there's a two-fold goal here. One is to return every mess to a single omega mess with no dependence and no dependency so that I don't have to change that code. I don't know enough to fix that mess. My goal is to hide it. But I also wanna take that meta knowledge and the knowledge of number and make my app not dependent. And I wanna be able to change from three to six to 10 shocks and have it not affect any of the code except the new code that has to be written to do that one thing. And so the first refactoring returned the omega messes. The second refactoring removed the case statement. And the case statement has bound into it a lot of knowledge, right? Every branch knows something. And replacing it with a factory, that knowledge is now in the factory, not in the case statement. More questions? So his question was about, I used the, sorry, I'm going blind. I used the convention that relied on the, being able to assemble the name out of a string to generate the actual class name. If I had time, I would have done 10 different factories, right? Think of all the things you can do. You can write a hash that has some string or symbol in it where you look up the class name. You can write a hash where on the left side is a method that you evaluate that returns true or false that tells you whether to use the class side on the right name. There's the number of ways that you can use information to generate the name of the class is almost infinite. And different situations call for different ones. And you should use the one that's appropriate where you are. Whatever piece of information you have ought to be able to be something that you can look up dynamically to figure out the class of the object you want. Yeah. In a longer version of this talk, there was an example for that used inheritance to solve this problem. If you read all the blurbs, like everybody says, prefer composition instead of inheritance. And they say that because there's a set of dependencies inheritance where you can drive yourself into a corner that's hard to get out of. The decision between composition inheritance is basically one of degree. If the shock class does a lot of things and it's composed of different kind of costars and maybe different kind of other things and maybe different kind of other things where you decide a bunch of roles and you plug other objects in behind those, perhaps composition is the right solution. If shocks are almost all alike, the entire body of shock, like the whole set of shocks I have is almost identical. And they vary in tiny little specializations where I can use the template method pattern to supply those specializations. Then inheritance is a really appropriate solution. As a matter of fact, if I had this problem and my shock was big, I would probably use inheritance to solve it. So I know inheritance has a bad name, but I like inheritance. I'm like an old small talker. I like inheritance. I'd probably overuse it a little, but I'm really careful as I go down the inheritance path not to go too far before I switch it back to composition if it turns out it's not the right solution. Thank you. Oh wait, one more. Instead of passing the class name, you absolutely could have. In my case, I wanted to have a level of indirection. Like it could be that the user picks two different things and they both map to the same. Well, that wouldn't have worked in my example, but having a break between the two sometimes is nice because you have that level of indirection. You can do things at that boundary, but you absolutely could do that, right? You could have the very front end. Very often the solution to this problem, like I want to send a message to an object and my problem is not the message, my problem is constructing the right object and that construction moves back further out, right? Like if someone could have somewhere, somehow constructed the right object earlier, they could just inject it into this class and I don't have the factory problem here, though it does exist at some place higher in the chain. I love these questions, I answer every one of them. It's really great. Thank you, sir. Thank you. Thank you.