We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Nothing is Something

00:00

Formal Metadata

Title
Nothing is Something
Title of Series
Part Number
88
Number of Parts
94
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Our code is full of hidden assumptions, things that seem like nothing, secrets that we did not name and thus cannot see. These secrets represent missing concepts and this talk shows you how to expose those concepts with code that is easy to understand, change and extend. Being explicit about hidden ideas makes your code simpler, your apps clearer and your life better. Even very small ideas matter. Everything, even nothing, is something.
Electronic mailing listSlide ruleDifferent (Kate Ryan album)Multiplication signProcess (computing)Mereology2 (number)Goodness of fitPressureLevel (video gaming)Set (mathematics)Social classObject (grammar)QuicksortState of matterVideo gameProgrammer (hardware)Greatest elementCodeMathematicsObject-oriented programmingComputer animation
Message passingLie groupObject (grammar)BitSpacetimeParameter (computer programming)Real numberInstance (computer science)Greatest elementSocial classCodeElectronic mailing listMathematicsSymbol tableBoolean algebraNumberReading (process)Computer animation
Multiplication signElectronic mailing listFormal languageBoolean algebraComputer animationLecture/Conference
ExpressionResultantBlock (periodic table)Performance appraisalBitType theoryCodeWordMessage passingCuboidRight angleRankingMeasurementComputer animationLecture/Conference
Message passingDecision theoryWritingCondition numberObject (grammar)Formal languagePower (physics)CASE <Informatik>Procedural programmingWordDifferent (Kate Ryan album)Computer animation
Open setMessage passingSocial classInstance (computer science)Block (periodic table)Control flowCodeBit rateImplementationRight angleComputer animation
Pointer (computer programming)Boolean algebraMessage passingObject-oriented programmingCodeArithmetic meanRight angleSocial classObject (grammar)Computer animationLecture/Conference
Object (grammar)Formal languageStatement (computer science)Condition numberState of matterObject-oriented programmingComputer animationLecture/Conference
Electronic mailing listObject (grammar)Factory (trading post)Social classCondition numberIntegrated development environmentArchaeological field surveyKey (cryptography)Computer animation
Pointer (computer programming)Right angleObject (grammar)Message passingString (computer science)Line (geometry)CASE <Informatik>Condition numberPoint (geometry)CodeForm (programming)Core dumpMereologyType theoryProgrammer (hardware)BitElectronic mailing listComputer animationLecture/Conference
Condition numberString (computer science)CodeRight angleSurgeryCall centreComputer animation
Object (grammar)Electronic mailing listMessage passingCodeArithmetic meanRootComputer animation
Level (video gaming)Social classObject (grammar)ResultantCodeStudent's t-testVideo gameRight angleElectronic mailing listPattern languageMathematicsComputer animation
System callPattern languageTerm (mathematics)Condition numberObject (grammar)Computer animationLecture/Conference
Object (grammar)Pattern languageInstance (computer science)MereologyElectronic mailing listCodeMessage passingCondition numberSound effectLogicSystem callRight angleDisk read-and-write headAbstractionAtomic numberSheaf (mathematics)Task (computing)Computer animation
LengthOrder (biology)AbstractionString (computer science)Line (geometry)SpacetimeFrequencyCartesian coordinate systemBitMultiplication signNumberLoop (music)CodeLetterpress printingoutputSocial classWebsiteMoment (mathematics)Term (mathematics)FreewareCASE <Informatik>Parameter (computer programming)Computer animationLecture/Conference
Function (mathematics)RandomizationResultantEqualiser (mathematics)Control flowCondition numberInheritance (object-oriented programming)Computer-assisted translationRevision controlMetropolitan area networkImplementationBitMultiplication signCASE <Informatik>VarianceTable (information)Arithmetic meanProcess (computing)Right angleCodeAttribute grammarComputer animationLecture/Conference
Right angleMereologyConnected spaceMessage passingParameter (computer programming)Array data structureCode refactoringFunction (mathematics)BitDependent and independent variablesNumberPairwise comparisonRandomizationTask (computing)Wave packetInheritance (object-oriented programming)CodeSound effectStatement (computer science)Network topologyArmGodEndliche ModelltheorieRow (database)MathematicsComputer animationLecture/Conference
RandomizationRight angleForm (programming)QuicksortMereologyCodeWritingEndliche ModelltheorieSlide ruleAxiom of choiceContent (media)Module (mathematics)Inheritance (object-oriented programming)Computer animation
Right angleObject (grammar)FLOPSSurfaceAreaMathematical analysisProgram slicingInheritance (object-oriented programming)SineFlip-flop (electronics)Computer animation
CodeRight angleComputer fontWordInheritance (object-oriented programming)RandomizationComputer animationLecture/Conference
ParadoxMathematicsIdentity managementTransformation (genetics)BitGreatest elementComputer animation
SpreadsheetOrder (biology)Right angleEmailSocial classStudent's t-testWater vaporRow (database)AlgorithmDigital electronicsInstance (computer science)CASE <Informatik>System callComputer animation
Right angleOrder (biology)Object (grammar)Bounded variationDependent and independent variablesParameter (computer programming)ImplementationOnline helpSpacetimeSemiconductor memoryTunisMetropolitan area networkTheoryRankingComputer animationLecture/Conference
InjektivitätBitExecution unitLine (geometry)MereologySet (mathematics)CausalitySemiconductor memoryFile formatGateway (telecommunications)Game theoryRandomizationComputer animation
Figurate numberOrder (biology)File formatBounded variationSound effectMultiplication signVideo game consoleSocial classArithmetic meanWordComputer animation
InjektivitätCodeMessage passingSpacetimeLine (geometry)NumberCartesian coordinate systemAbstractionCondition numberInheritance (object-oriented programming)Social classScaling (geometry)Pointer (computer programming)Object (grammar)Pattern languageRight angleBoolean algebraMobile appWordShared memoryRule of inferenceSheaf (mathematics)Arithmetic meanLogicSymbol tablePolymorphism (materials science)Term (mathematics)Multiplication signComputer animation
Electronic mailing listBeta functionComputer animationLecture/Conference
WebsiteMultiplication signInformation privacySign (mathematics)Library (computing)
Transcript: English(auto-generated)
Thank you for coming, I'm Sandy Metz. I was a programmer for 35 years, I'm a woman of a certain age, 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
and boy that was hard, curriculum is tough. And so I sat down and I 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 been teaching that course for the last year and a half and I finally realized after, 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 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 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 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 gonna involve a lot of slides.
I calculated by my math I'm gonna 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, this talk's in four parts
that's gonna build up from the bottom. In this first part, after Pooda 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 gonna 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 gonna be Ruby code. It's that. Now you might not be familiar with this. Right, there's a send method in Ruby 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 fixnum and what do fixnums 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. Make it easy because Ruby's friendly that way. Now, 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 as 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? Well, it knows these things. And so now I'm gonna ask you to believe the useful lie. Right, true and false and Ruby
are a little bit more nuanced than this, but let's pretend just for the sake of this talk that true's just an object and I'm gonna 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's an instance of true class.
You send a message. That's how you talk to it. That's how booleans 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 keywords 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 booleans. Now this is so unquestioned in most of our minds that the explanation I'm about to give you is gonna sound really weird. Here's how you use that special syntax. There's an expression that gets evaluated and based on the result of that expression,
one of the following blocks is gonna 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 gonna evaluate the block that is after the word else. So 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 gonna evaluate the code before the else, the block of code before the else, otherwise I'm gonna 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 wanna have the syntax, I just wanna send a message to an object. And I don't wanna 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 gonna have to break open true class because this, I'm sure it'll be fine.
And so I'm gonna implement an API. The API is these two messages. I'm gonna 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 gonna yield to the block.
Now I'm gonna return self, just ignore that for now. It'll be clear later while we did it. The if false method, if false is implementation, true class does nothing. It does not yield to the block. And so then if you, once you see that, you can pretty much guess what has to go in false class. It's just the opposite, right? True's gonna yield in the if true method.
False is gonna 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. Alright, 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 gonna ignore the block. And if I send if false to an instance of false, it's gonna evaluate the block. It's totally easy. That's how it works. Now, it's not quite right because we really need truthy falsy and that's so easy to do.
Let's just promote this up to object and I'll duplicate this code in nil class. Alright, 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. How would you think about objects if there were no if statement? What would it mean to you 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, unrepentingly, sorry, unrepentantly condition a verse. I hate them, all right? I just hate them. I grew up without them. And here's a condition I really hate. We have an animal class. It has 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 animal 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, if you 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, all right? 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, right? I get the right list back if I try to talk to them now. But of course we're Ruby programmers and that's way too ugly. So we're gonna start doing this, right? I'm gonna use truthy, the truthiness to make that a prettier line of code.
And of course it means I've lost my ability to provide, to substitute that string in there. But it does work, it does not blow up. But of course if you're a Rails programmer, we gotta 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 a bit more because I want that. And that is this, and that's what I was complaining about in part one.
All right, but it's even worse. So this is a general case, right? 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 gonna have is this. It's gonna be all over. And the day you decide to change that value, you're gonna 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 wanna know these things. I just wanna send a message to an object. I wanna 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.
If only I had something like that. If I had that object, okay, I would prefer, this is the first really high-level idea. I would prefer to know about the name of a class, to know an object, than to duplicate that behavior everywhere. And so if I can create that object,
here's how I'd 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 understands how to respond to me. All right. Did this improve the code? Well, I just added it to Pensy. Awesome. Still have it conditional.
All right, despite the fact that I'm hating on them, 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. All right, I can just talk to the object 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, right? It's been described. Some guy named Bruce Anderson made up a beautiful term for it. He calls it the act of nothing.
The act of nothing. Isn't that beautiful? I'd love that. And so if we do this, and I said, I conceded that we added a dependency and we have that condition, but once you get here, well, let's do it here. Let's do this. I told you I would prefer to know an object rather than duplicate behavior, but it is also true. 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. Okay, yes, I concede that we're doing that, right? 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 every one of them like they're the same thing, and the list just works. This is awesome.
It's the null object pattern, and it makes 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, all right, 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. And so this section, part four of this talk, which is approximately the length of everything you've seen so far, is gonna explain that next abstraction. In order to go here, I'm gonna have to switch examples.
So, the house that Jack built. All right, this is a tail 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, so every line gets longer and longer.
Eventually, you get to this. If I ask you to write the Ruby code to produce this tail 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 last on array, like if you send the last to the array, you get the last thing back. It takes an argument, so you can get the last in number of things out of an array. So in this case, if I pass three to last to data, I would get the wrap at eight, the malt that land, the house that 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 and 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 wanna recite the whole tail and print all the lines, I'm gonna have to loop as many times as I have 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 one is gonna be that. Line at two is this. Line at three is that. Line at 12 is that. I can do the whole thing with recite. Alright, so let's imagine that you've written this, right? This is, you have an application that has whatever for whatever reason they've asked for this. They love you for having done it.
You did it quickly. It totally works. And of course now, they want something new, alright? They want house, right? 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, they're different every time, but in this case, it ends with the rat that ate the made in all fluent in the milk, the cat that killed. Alright, and so the tail, the random version of this tail would be this is the rat that ate, this made in all fluent in the milk, the rat that ate,
this is the cat that killed the made in all fluent in the milk, the rat that ate, and then the whole thing would be like this. And you, I suspect from your laughter, have 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, alright? And inheritance is really attractive here because it totally works, watch, right? Subclass house, override data, shuffle is a method on array that randomizes it. I'm gonna 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, made in all fluent, cat that killed, if you look in there somewhere, that priest is marrying a man who's kissing a 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, right?
They want something else, of course, right? You're incredibly successful. They want, 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 malt that lay in the malt that lay in,
the house that Jack built, the house that rat that ate, the rat that ate, the malt that lay in, right? So, that's what we want, echo house. Now, I'm gonna have to do a slight refactoring before we get going here. Really, that's the bit I need to change. I have this 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 gonna do a tiny bit of refactoring, right?
I'm gonna isolate that bit of code. I'm gonna just put it down. Oh my God, the part's naming is so hard, so I'm gonna call that parts, then I'm gonna send the message parts there, then I 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 if I could 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 statements. What are you gonna do? I'm waiting on the train to go by. It's been going by during the keynotes. How are you gonna do it? 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 gonna override house. I'm gonna 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 compress, 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 can 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, got random house, overrides data. I got echo house that overrides parts.
Can anybody guess? Oh, here it comes, yeah. Random echo house. All right, so we don't even need house anymore, because that's really not the problem, all right? So, what are you gonna do?
You're screwed, and don't tell me, Do not insist to me that modules is the solution to this problem. It's not, and 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. Here, once you get on the inheritance path, I want this sort of cross-pollination
of two subclasses I already have. Just stop that. We are not using multiple inheritance here. It is 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, inherit parts and duplicate data, all right? Now, both of those choices are so obviously wrong.
They're so obviously misleading that when I go places, and I see people who've encountered this problem, very often they choose neither one. They do not 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, right? 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, right? 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. An 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, and you try to inherit from it, you get these things. You cannot get that. Poof! Don't we love effects? You cannot get it, and if you flip-flop it, wait for it, poof, 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. There's a better idea here. All right, no, not that, okay? And 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. Now, we have this bargain that subclasses are ISA, right? 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, right? So we fooled ourselves because of the names we chose.
Names are incredibly important and 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, all right? 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 gonna make that method as much like this method as I can. I'm gonna make these data methods as identical as I can do. So first I have to get that out of it. I'll just put that in. I'll put the actual array in a constant. I'm gonna implement data just to return it.
So if I'm looking at the thing down there, now I can keep on doing transformations like. I could 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 hand on the concept
but it's much easier to see what changed. We have to figure out what that thing is and give it a name. And I was lucky enough to teach with Abdi 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? And we 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 Orr. Okay, I see these guys. You're like too nerdly. That's your problem. Too much nerd cred. It's the thing that we're changing is Orr, right? And so if Orr 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 Orr is a household that is so clearly wrong that it's absolutely wrong. Orr is a role. And so let's write the Orr roles. Here's one. Right? I'm gonna make Random House, I'm gonna call the API Orr. I'll have it take an argument that's the array
and it's gonna shuffle it. What's the implementation here? Yeah, it's an algorithm. It's real. Alright, and so if I want to use this I'm just gonna throw, let's throw that subclass away. That didn't help. That went badly wrong, right? I'm just gonna move this code around. I'll put an attr 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 gonna do is we're gonna remove the responsibility for ordering that array from house. I'm gonna do it by using this order, right? So I'm gonna inject it. It's a name parameter, right? I'm gonna inject an order and I'm gonna give that order an opportunity to order the data.
And it's whatever it does is what house is gonna 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, all right?
This is composition, all right? We're trying to inject an object to play the role of the thing that varies. How are we gonna 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 gonna inject them
right, we love dependency injection. I'm gonna put this, okay wait, here, I have to do that again, sorry. It's gotta line up, kills me. So I'm gonna keep the form, and I'm gonna 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've 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 no longer have more code, we have actually less code. And there is not one bit of duplication in here. We've made this units of pluggable behavior.
Before when it was inheritance, it looked like this. But really, what we want, if there's a specialization, there's, by definition, there's never just one specialization. The new thing is one thing, but the old thing that you're specialized 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 gotta 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, somebody is gonna inject the player. Here, wait, let's do that again, too.
I love it. It is really one of the few constellations 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 gonna 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, and 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 as we move out in 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, and 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's really easy to see that real things could be modeled as objects, right? The chair that you're sitting on, the chair that you're in, the person beside you, and it didn't take long once you 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 fictionums 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? But inside my app, that six and the true are as real as the chair.
Now, 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 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, and if you 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 seeing you, Metz.
To 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 privacy 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.