 Thank you for coming. I'm Sandy Metz. I, um, I was a programmer for 35 years. I'm a woman of a certain age. I, I really am. I wrote a book a couple years ago when it came out. It was like a bomb went off in my life, and I had to quit my day job. I was so busy doing other things, and because I quit my day job I needed a way to make a living, and people kept asking me to teach, so I finally broke down and agreed to teach, to teach short object-oriented design classes. And so then I had to make up curriculum. Boy, that was hard. Curriculum is tough. And so I sat down and I, uh, figured out what I thought were the most important set of lessons I could teach in three really intense days. And when I did that, when I made up the curriculum, I thought that they were all unrelated. I thought they were completely unrelated lessons. Good things, yes, but not the same thing. And then I've, I've been teaching that course for the last year and a half, and I finally realized after, uh, since now, actually, now my job is to think about code. What a wonderful thing at this sort of advanced stage in my career. I'm not driven by, uh, deadline pressures every day. I really get to reflect about what makes code good, and how we can make good code, and how to explain to people what, what, how to do that. And so in the process of making this course, I've taught it over and over again, and I've got the leisure to reflect deeply about code. And I finally realized 18 months later that I didn't, wasn't really teaching a bunch of different ideas, that I was really teaching one idea. One simple idea. And I finally understand it. And so today's talk is everything I've learned in the last year and a half, in 30 minutes. And that, it's going to involve a lot of slides. Uh, I, I calculate, by my math, I'm going to change slides about every four and a half seconds, once we get going. If you want to change seats now, it's not too late to move over to the sides. But if you're here, okay, you've been warned, right? That's all I can tell you. So, um, this talks in four parts that's going to build up from the bottom. In this first part, after Puder was published, it became really clear to me that, it was surprising to me to find that I had a different idea about objects than many of the people in our community. And I was curious about why that was. And when I thought about it, I realized it was because, I decided that it was because I'm infected by small talk. I've been writing Ruby since 2005, and I have not yet written Ruby for as many years as I wrote small talk. Now, I say infected because it's funny on the slide, but really I think of myself as inoculated by small talk. I'm going to show you just one small thing about small talk today that will make it more clear how I think about objects. Here's the thing, this is all going to be Ruby code. It's that. Now, you might not be familiar with this, right? There's a send method in Ruby that, that sends a symbol that invokes the method named in the symbol, and that does the same thing. We also have this. You may be surprised to see that it is this, and it does the same thing. Now, what is one? Well, one is a fix num, and what if fix nums know? They know this, among other things, this list, and you can see on this list that we have that. Now, what this means about Ruby is that this is what's real. This is the truth at the bottom of all things. We're just sending a message to an object. That when you say one space plus space one, that is a special syntactic sugar put on top of message sending. That's unique. This is normal. That's special. This is real. And this is just here so it'll look like math, right? Doesn't really matter. They want it to look like math to make it easy because Ruby's friendly that way. You notice on this list we also see that, and I don't have to tell you now, I hope I don't have to convince you that I'm sending equal equal to one and passing one is an argument. When I do that, I get back this. Now, what is that? Well, it's the singleton instance of this class. And what does that class know? It knows these things. And so now I'm going to ask you to believe the useful lie. True and false in Ruby are a little bit more nuanced than this, but let's pretend just for the sake of this talk that true is just an object and I'm going to deal with it by sending messages and that Ruby behaves that way. I was unsurprised by this when I came to Ruby, right? True, sure, true is an object. It isn't true class. You send a message. That's how you talk to it. That's how Boolean's work. I was unsurprised by that idea when I came to Ruby from Smalltalk, but I was extremely surprised by another thing about Ruby. And it was that Ruby had a special syntax for dealing with this object. It was very confusing to me in an OO language that there was a special syntax for this. This is a list of the key words in Smalltalk. And no matter how many times you count them, there will still be six. Here's Ruby. And if you look at that list, you'll notice among other things on it, this thing. This is special syntax for dealing with Boolean's. Now, this is so unquestioned in most of our minds that the explanation I'm about to give you is going to sound really weird. Here's how you use that special syntax, right? There's an expression that gets evaluated. And based on the result of that expression, one of the blocks, one of the following blocks is going to get evaluated. If it evaluates to true, I'm going to evaluate the block that is before the word else. And if it evaluates to false, I'm going to evaluate the block that is after the word else. So this is really, it's really saying this, right? But really it's actually a little bit more complicated in Ruby because we have truthy, right? If it's truthy, I'm going to evaluate the code before the else, the block of code before the else, otherwise I'm going to evaluate the block of code after the else. This is a type check. And we don't do type checks in OO, right? We hate this idea. I don't really, I don't want to have the syntax, I just want to send a message to an object. And I don't want to have to look at the kind of thing it is and make a decision and then choose between two different kinds of behavior. If you came to Ruby and OO from a procedural language like most of us did, it probably seemed normal and reasonable to write long conditions and start with if or case or whatever. But I can promise you that the very presence of this keyword in our language makes it easy to retain that procedural mindset and it keeps you from learning OO and taking advantage of the power of objects. And to show you how unnecessary this keyword is, let's just change Ruby to have small talk like syntax for dealing with conditions. Message sending syntax. Here we go. First we're going to have to break open true class because I'm sure it will be fine. And so I'm going to implement an API. The API is these two messages. I'm going to do if true and if false. So in true class, the if true method, now I'm taking advantage of the fact that all these methods take an implicit block. The if true method in true class is going to yield to the block. Now I'm going to return self, just ignore that for now. It'll be clear later while we did it. The if false method, if false is implementation, the true class does nothing. It does not yield to the block. And so then once you see that, you can pretty much guess what has to go in false class. It's just the opposite. True is going to yield in the if true method, false is going to yield in the if false method. And so if you break the classes open and make this monkey patch, now you can write this code. If I got a true, remember that if true does the yield, so it'll evaluate the block. If I send that message to a true, if I send that same message to a false, nothing happens. And the opposite is true when I'm dealing with falses, right? If I send if true to an instance of false, it's going to ignore the block. And if I send if false to an instance of false, it's going to evaluate the block. It's totally easy. That's how it works. Now, it's not quite right because we really need truthy-false, and that's so easy to do. Let's just promote this up to object. And I'll duplicate this code in nil class. If I do that, if you write this code, which, by the way, was shamelessly swiped from you to cats, thank you, Yehuda, if you're here. Now I can do anything. Everything is true, and nil and false are false. That's all it takes. You don't need this special syntax. I can now replace this with that. And you can see here, this is why they return self, so they can be chained together. And I can replace this with that. It doesn't need, we do not need a special syntax in an object-oriented language to deal with Booleans. We can just send messages to objects. We can do the normal thing. Now, having shown you this, I am not suggesting that we do it. I'm not. Don't tweet that. I'm not. But what I want you to do is I want you to think about what it would mean to you. I want you to think about objects if there were no if statement. What would it mean in your conception of how to write object-oriented code? The fact that I was trained in OO by a language that did not have an if statement made me permanently, irrevocably, and unrepentantly condition averse. I hate them. I just hate them. I grew up without them. Here's the condition I really hate. This is a factory method find. It takes an ID that returns an object. If you pass it an ID that it doesn't know, it gives you back nil. If you happen to get an array, perhaps you don't even know what keys are in this array. You get this array. You call in and we'll find on all the objects. You get back this list and you start talking to them. Boom. All right. And so, before I go on, I want to concede that sometimes nil is nothing. And if nil is nothing, do not care about that nil. Right here, you can do this. You can throw the nil away. You can compact the array and then when you talk to the objects, it all works. However, if you're sending it a message, nil is something. And what we have to do is we want to fix it here so that it doesn't blow up when the nil comes. Now, often what happens at this point in our code is that we put a condition right there. We add this condition. This is the most verbose form of it. This is a case where I want to say no animal when there's a nil. This is like saying guest when there's no user logged in. This is that exact situation. So in this case now, they all respond. I get the right list back if I try to talk to them. But of course, we're Ruby programmers and that's way too ugly. So we're going to start doing this. I'm going to use truthy, the truthiness to make that a prettier line of code. And of course, it means I've lost my ability to substitute that string in there. But it does work. It does blow up. But of course, if you're a Rails programmer, we got to try. Now, I'm not saying not to use try. I use try myself. But let's be honest about what's going on here. This is really that. Which is really this. Which is really that. And that, if you write it all out, looks like this. And that, okay, it's remenorers because I want that. And that is this. And that's what I was complaining about in part one. But it's even worse. So this is a general case. Code to do some stuff, code to do some other stuff. Here it's actually worse because what we're saying is if I know what type you are, I will supply the behavior. Otherwise, I'll send a message. This is absolutely terrible. And the core problem here is because conditions always get worse. Conditions reproduce. If you put that string, no animal in your code, what you're going to have is this. It's going to be all over. And the day you decide to change that value, you're going to end up with doing a thing they call shotgun surgery. It's everywhere. So I hate these conditions. I'm extremely condition averse. What I am instead is message centric. I don't want to know these things. I just want to send a message to an object. I want to send this message. And now the problem here, the root of the problem is that sometimes I get this object back. And it knows name, but then sometimes I get that other object back and it does not. The objects on the list that get returned to me conform to different APIs. What I need down here is something to which I can send the name message and get back the value no animal. And so let's write the code we wish we had. And only I had something like that. If I had that object, this is the first really high level idea. I would prefer to know about the name of the class, to know an object, then to duplicate that behavior everywhere. And so if I can create that object, here's how I would use it. You can bar bar it in right there. And if you do that, it'll change this list so that everything on it will change. All right. Did this improve the code? Well, I just added it at Pensy. Awesome. Still have it conditional. Despite the fact that I'm hating on it, it's still there. But something is better and it's this. I no longer own the behavior. And that means all of this code down here can disappear. And I can do that. And now thankfully I can also do this. I don't know what I got back and the results are correct. Everything now works. Now this thing, this idea, these objects, this concept has a name. And it's called the null object pattern. It's a famous pattern. It's been described, some guy named Bruce Anderson made up a beautiful term for it. He calls it the active nothing. The active nothing. Isn't that beautiful? I love that. If we do this, and I said I conceded that we added a dependency and we have that condition, but once you get here, I would prefer to know an object rather than duplicate behavior, but it is also to the corollary to that, is I don't want to know very many objects. But once we get here and isolate that behavior in an object by itself, well, sorry, here. I concede that we're doing that. But it's really easy to fix because you can take that untrustworthy external API and you can wrap it in your own object. You can catch that message and forward it on and you can put the condition right there in that one place. And then all the places in your code where you have to do this, you can now just call your own trustworthy API. And when you do that, you get this list back and you can talk to everyone of them like they're the same thing and the list just works. This is awesome. This is a dramatic improvement in the code. And if that's the only thing you can take away from the talk, like if I lose you in the next section, not that I'm saying I will. If you can take this home and use it, it will improve your code. But the thing I have learned in the last 18 months is the null object pattern is a small concrete instance of a much larger abstraction. It is an example of a really simple idea that's very, very large. I'm going to go through this talk, which is approximately the length of everything you've seen so far is going to explain that next abstraction. In order to do it, in order to go here, I'm going to have to switch examples. So the house that Jack built. This is a tale that kids learn it's cumulative. So there's bits in it where you get a new bit every time and you stick it in in front of the bits that are there. It has 12 different bits, eventually you get to this. If I ask you to write the Ruby code to produce this tale in such a way that you did not duplicate any of the strings, you would probably do something like this. You would take all the bits and you'd put them in an array. Maybe put that in a method. Probably have some kind of phrase method. Now you may not... You're probably familiar with the last on array. Like if you send the last in array, you get the last thing back. Or if things out of an array. So in this case, if I pass 3 to last to data, I would get the rapid 8, the multiland, the house of Jack built back. That phrase method also turns that array back into a string by joining on spaces. I need a way to put the this is in the front in the period at the end. So I probably have some kind of line thing that takes a number. And then it also calls phrase to get that middle bit. If I want to recite the whole tale and print all the lines, I'll put all the bits, call a line for each one and put a new line at the end. I'll probably put the whole thing in a class. And if I write that code, I can do this. Line at 1 is going to be that. Line at 2 is this. Line at 3 is that. Line at 12 is that. I can do the whole thing with her site. All right. So let's imagine that you've written this. Right? You have an application that has whatever for whatever reason they've asked for this. And of course now, they want something new. All right. They want house. We're not getting rid of house. We're adding a new feature, a new kind of house. And this is called random house. And here's the spec. They want you to take this array of bits and one time before you start producing any of the lines, they want you to shake it up. They want you to randomize it. Now you notice in this case, this one random version, the rat that ate, the maiden awful in the milk, the cat that killed. All right. And so the tail, the random version of this tail would be this is the rat that ate, this maiden awful in the milk, the rat that ate. This is the cat that killed, the maiden awful in the milk, the rat that ate. And then the whole thing would be like this. And you, I suspect from your laughter, I've already noticed that many variants of this seemingly innocent tail are not safe for work. So all I can say is that it's an equal opportunity offender. So South Park, South Park from here on. So if you were, so here's your job, right? You cannot break house. And I want you to do, I want you to implement random house without using any conditionals. No conditionals. Can't use a conditional. So you're probably thinking inheritance. All right. And inheritance is really attractive here because it totally works. Watch, right? So I'm going to go back to our data. Shuffle is a method on array that randomizes it. I'm going to have to cache the result because I only want to shuffle it once and then produce the whole tail. If you write this code, it totally random house works, right? Rat that ate, maiden awful in the milk, cat that killed, if you look in there somewhere, that priest is marrying a man who's kissing the horse. So that took about two minutes to write, right? And so, and they think you're a total genius. And so what is the next thing that happens? They want something else. Of course, right? You're incredibly successful. Now they want something called echo house. Here's how echo house works. Every bit, the bits get duplicated as they go in. So it's got this echo effect. This is the house that Jack built, the house that Jack built. Right? The mall built land, the mall built land, the house that Jack built. The rat that ate the rat that ate the mall built land. So that's what we want, echo house. Now, I'm going to have to do a slight refactoring before we get going here. Really, that's the bit I need to change. There's a method that's got more than one responsibility, which makes it hard to reach in here and change just one thing. So before I implement echo house, I'm going to do a tiny bit of refactoring, right? I'm going to isolate that bit of code. I'm going to just put it down. Oh, my God, the parts of naming is so hard. So I'm going to call that parts. And I'm going to send the message parts there. They don't care about phrase anymore. So if I could change, right now this method returns that if the number is three, this is the array I get back. Echo house would work somehow change it so that I got that back instead. All right, and as I said before, your task is to do echo house, but you may not use if statement. What are you going to do? I'm waiting on the train to go by that's been going by during the keynote. How are you going to do it? Well, we all know how we do it, right? We're already going down the inheritance path. And it turns out, it is incredibly easy to solve this problem with more inheritance. I'm in an override house. Subclass house override parts. I'll talk about that in a second. So super, if number is three, super gets this. I called super twice. Zip is not what you think. Zip is not compressed, it's zipper. Right? So it does this pairwise connection of those two arrays. And then although it is not necessary to flatten it, I cannot stop myself from doing it. Because I don't know it's supposed to be a one-dimensional array. You could be forgiven if you make the other argument. I would certainly forgive you if you did. And so that code, if I were to write that, Echo House totally works, right? And it took me about three minutes. And my customers think I'm a genius. It is awesome. This is why inheritance is so seductive, right? So here's what we have. House. Random house, overrides data. Echo House overrides parts. Can you make a guess? Yeah. Random Echo House. So we don't even need House anymore, because that's really not the problem. So what are you going to do? You're screwed. And don't tell me, do not insist to me that modules is the solution to this problem. I didn't have 100 more slides to prove it. Go write that code, right? You can dry out the code, but it is not the real solution to this problem. It's just another form of inheritance. I like this sort of cross-pollination of two subclasses I already have. Just stop that. We are not using multiple inheritance here. It's not the right solution for this problem. I can override Random House, I can inherit data and duplicate parts, or I can flip-flop it and inherit parts and duplicate data. Now, both of those choices are so obviously wrong. They're so obviously misleading that when I go places and I see people who have encountered this problem, very often they choose neither one. They don't choose to duplicate some of the code. What they do is they choose to duplicate all of the code. And I am sympathetic to the contention that that is more correct. They just override House and they copy it all down there. And there is a way in which that is more honest than putting it on one side or the other. Okay, so this is all... It seems so good and it has gone terribly wrong. Now, I want to draw you a picture. I think that will help illustrate exactly what's going on here. So this is a surface area House. This is not UML, it's just a little pointy thing. It feels like Random House is that big and that Echo House is that big. But really the truth here is that Random House is that big and it contains everything else that it did not specialize out of House. And Echo House is that big and it doesn't really matter. We can just throw House away. We have objects of this size, of this surface area. And if you come over here from it, you get these things. You cannot get that. Don't we love effects? You cannot get it. And if you flip flop it... Wait for it. It's right? It doesn't work. Really, if you think, okay, what I want is multiple inheritance. It is not the solution to this problem. We do not want multiple inheritance. This is not what's going on here. And if you go down the inheritance path because it seems easy, don't compound your sins by going further down that path. We have a better idea here. No, not that. The words for this is that inheritance is for specialization. It is not for sharing code. It is not for sharing code. It's a specialization. We have this bargain that some classes are is a. That's the relationship. Random House is a House. And if I were to ask you, is Random House a House, you would say, well, yeah, it's House, Random House. Of course it's a House. We have to figure out ourselves because of the names we chose. Names are incredibly important. They can be incredibly misleading, right? Instead of saying that, instead of being trapped by our bad name, let's do this. What changed? It's really hard to glance at that code and answer that question in an instant. And now we're going to do the next big pro tip. This is a paradox. You can reveal how things are different by making them more alike. Let's do that with this code. I'm going to make that method as much like this method as I can. I'm going to make these data methods as identical as I can. So first I have to get that out of there. I'll just put that in. I'll put the actual array in a constant. I'm going to implement data just by to return it. So if I'm looking at the thing down there, now I can keep on doing transformations like I can put that there. It's not necessary, but it would work, right? And I can take the super out on the bottom and replace it with data. Now, it's still a little bit hard to put your handle on the concept, but it's much easier to see what change. We have to figure out what that thing is and give it a name. I was lucky enough to teach with Obdi Grimm in the fall and he suggested to the students who are dealing with this problem that we pretend it's rows and columns in a spreadsheet. Write them down like this and then label every column. What is the header of the column? Well, that's the class. I suck at names, so let's call that data. The interesting question here is what is this? Let's call the subclass random house, but this is not random. Like, random is an instance of whatever this thing is, right? What is it? It's order. Okay, see these guys, you're like too nerdly. That's your problem. Too much nerd cred. The thing that we're changing is order, right? And so if order is that thing, then this is not nothing. This is an algorithm. And it's just as valid as the other one. And so now that you know its name, if you ask order as a household, that is so clearly wrong. That is absolutely wrong. Orders are roll. And so let's write the order rolls. Here's one, right? I'm going to make random house, I'm going to call the API order. I'll have it take an argument that's there, right? And it's going to shuffle it. What's the implementation here? Yeah, it's an algorithm. And so if I want to use this, let's throw that subclass away, that didn't help. That went badly wrong, right? I'm just going to move this code around. I'll put an adder reader in for data. And you know what's in that, so we can get rid of that. I need the space. So what we're going to do is we're going to remove the responsibility for ordering that array from house. I'm going to do it by using this order, right? So I'm going to inject it. It's a name parameter, right? I'm going to inject an order, and I'm going to give that order an opportunity and it's whatever it does is what house is going to have. And it works. So what have I just done? Well, I got more code to do exactly the same thing. And this is why people complain about object-oriented design, right? Because they can't think further ahead than this. But watch this. I got these, too, right? I got another kind of order. Why don't I just inject that instead? And that just works. This is composition, all right? We're trying to inject an object to play the role of the thing that varies. How are we going to do echo house? You totally know. You see the answer to this problem already, right? I need something to do this. And then I need another thing to play another variant of this role. And if I have these things, I'm going to inject them, right? We love dependency injection. I'm going to put this... Okay, wait. Here, I have to do that again. Sorry. It's got to line up. Kills me. So I'm going to keep the format. I'm going to stick it way down here because I want to intervene in the most narrow part possible where your mileage may vary. But I put it there when I wrote this code. All right? And so now, again, I have exactly the same behavior I had before I created these two new objects and injected them. But I can also inject the other one. And now I got echo house. So I have defined two roles and they each have two players. And so instead of getting line 12, let's just recite the whole thing. So here's the set of things I can do, right? I can get house. I can get random house. I can get echo house. Or I can get random echo house. And we have not... We no longer have more code. We have actually less code. And there's not one bit of duplication in here. We've made these units of pluggable behavior. Before when it was inheritance, it looked like this. If there's a specialization, by definition, there's never just one specialization. The new thing is one thing, but the old thing that you specialize is something, even if it looks like nothing. So you have to isolate the thing that varies. You have to figure out what it is. That leaves a hole in house where you have to plug that stuff back in. Then you've got to figure out what its name is. Here we call them order and format. You have to define that role. We made APIs for the order and formatter. And then later at runtime, we inject the player. Here, wait. Let's do that again, too. Sorry. I love... It is really one of the few consolations in making talks, keynote effects. So this is composition plus dependency injection. And this is what it means to do object-oriented design. All right. Understanding these techniques lets you find the underlying abstraction. And getting that abstraction right is going to dramatically improve your code. If you're talking to nil, it's something. Use the nil object pattern. You're done checking for nil. Okay? Stop it right now. Make objects to stand in for those nils and use those active nothings. Next, beware of inheritance. It's easy to begin with. Maybe you should use it to start with. But it's a knife that will turn in your hand, especially if the amount of code that you specialize is a small proportion of the class that you're subclassing. Be very careful if you use it and be ready to switch to composition. It's not for sharing behavior. The bigger idea is we move out and scale here is there's no such thing as one specialization. When you see that you have a variant, it means that you have two. You have to isolate the thing that varies, name that concept, define the role, and then inject the players. When you got started in OO, when you first started writing OO, it was really easy to see that real things could be modeled as objects, right? The chair that you're in, the person beside you. And it didn't take long when she started writing code to figure out that more abstract things could be modeled usefully, right? Business rules, ideas, concepts, processes. And back in the beginning of this talk, I started with fix-nums and Booleans. And you might not have really thought, we're so used to those ideas that you don't really think of them as being abstractions, but they are not real. You cannot reach out and pick up a six or a true, right? Inside my app, that six and the true are as real as the chair. Now, the numbers are an amazing abstraction, but then there's a way that the abstraction in numbers doubles up, and that's with zero, right? We didn't have zero for a really long time. Zero represented nothing. And before we had the idea of zero, there were things we couldn't do. And then after we had the idea of zero, that concept became a polymorphic in our terms, right? We could use numbers in new ways once we discovered that the nothing in the number set could be represented by the symbol zero. So your applications are full of things like zero, right? There are concepts that are abstractions that reveal their presence by the absence of code. I don't want to write a condition. I just want to send a message to an object. I want to do OO that way you have to find those objects, and they're hidden in the spaces between the lines in your code. They seem like nothing, but they aren't. They're actually something always. It's true that they're something, because something, nothing is always something. Thank you. I'm Sandy Metz. Get on that list for the beta of the new book there. I'm teaching... I rarely get to teach public course, but the course I normally teach privately in New York City. Sign up on my website if you want news about that. Of course I have stickers, but even better, I got tats. So thank you again. Come and find me later. We don't have time for questions, so...