[Script Info] Title: [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text Dialogue: 0,0:00:17.30,0:00:19.41,Default,,0000,0000,0000,,MARK MENARD: So, many thanks to the organizers Dialogue: 0,0:00:19.41,0:00:21.35,Default,,0000,0000,0000,,here at RailsConf. This is my first time Dialogue: 0,0:00:21.35,0:00:23.89,Default,,0000,0000,0000,,talking at RailsConf. It's, frankly, Dialogue: 0,0:00:23.89,0:00:25.25,Default,,0000,0000,0000,,kind of intimidating to be up here Dialogue: 0,0:00:25.25,0:00:27.75,Default,,0000,0000,0000,,and see so many people out there. Dialogue: 0,0:00:27.75,0:00:29.55,Default,,0000,0000,0000,,My name is Mark Menard. I'm gonna be talking Dialogue: 0,0:00:29.55,0:00:32.62,Default,,0000,0000,0000,,about small code today. And I've got a lot Dialogue: 0,0:00:32.62,0:00:35.16,Default,,0000,0000,0000,,of code. About seventy-nine slides. A hundred\Nand thirty-seven Dialogue: 0,0:00:35.16,0:00:37.94,Default,,0000,0000,0000,,transitions. Not quite as much as Sandi had,\Nbut Dialogue: 0,0:00:37.94,0:00:40.48,Default,,0000,0000,0000,,it's a lot to get through. So let's get Dialogue: 0,0:00:40.48,0:00:41.68,Default,,0000,0000,0000,,going. Dialogue: 0,0:00:41.68,0:00:44.25,Default,,0000,0000,0000,,So, I'm just gonna let this quote up there Dialogue: 0,0:00:44.25,0:00:44.81,Default,,0000,0000,0000,,sink in. Dialogue: 0,0:00:44.81,0:00:51.81,Default,,0000,0000,0000,,So. All of us have that file filled with Dialogue: 0,0:00:53.25,0:00:56.61,Default,,0000,0000,0000,,code that you just don't want to open. As Dialogue: 0,0:00:56.61,0:01:00.56,Default,,0000,0000,0000,,you heard earlier, maybe it's your User class.\NThat Dialogue: 0,0:01:00.56,0:01:02.54,Default,,0000,0000,0000,,class that has comments like, woe to ye who Dialogue: 0,0:01:02.54,0:01:05.88,Default,,0000,0000,0000,,edit here. The problem with this code is that Dialogue: 0,0:01:05.88,0:01:10.86,Default,,0000,0000,0000,,it does live forever. It encapsulates business\Nlogic that Dialogue: 0,0:01:10.86,0:01:13.04,Default,,0000,0000,0000,,ends up getting duplicated elsewhere, cause\Nno one wants Dialogue: 0,0:01:13.04,0:01:15.45,Default,,0000,0000,0000,,to go in there and look at that code. Dialogue: 0,0:01:15.45,0:01:17.52,Default,,0000,0000,0000,,It's also very hard to understand. Dialogue: 0,0:01:17.52,0:01:19.97,Default,,0000,0000,0000,,I'm gonna be talking about ways to avoid this Dialogue: 0,0:01:19.97,0:01:24.58,Default,,0000,0000,0000,,situation. I'm gonna be talking about code.\NCode at Dialogue: 0,0:01:24.58,0:01:28.00,Default,,0000,0000,0000,,the class level and the method level. Having\Nsmall Dialogue: 0,0:01:28.00,0:01:31.02,Default,,0000,0000,0000,,code at the class and method level is fundamental Dialogue: 0,0:01:31.02,0:01:34.07,Default,,0000,0000,0000,,to being able to create systems that are composed, Dialogue: 0,0:01:34.07,0:01:37.60,Default,,0000,0000,0000,,composed of small, understandable parts. Dialogue: 0,0:01:37.60,0:01:39.11,Default,,0000,0000,0000,,I'm gonna lay out a few base concepts so Dialogue: 0,0:01:39.11,0:01:41.74,Default,,0000,0000,0000,,that we can start with a clean sheet and Dialogue: 0,0:01:41.74,0:01:44.34,Default,,0000,0000,0000,,on the same page. I think there's a lot Dialogue: 0,0:01:44.34,0:01:46.42,Default,,0000,0000,0000,,of problems with what people conceive of as\Nsmall Dialogue: 0,0:01:46.42,0:01:49.18,Default,,0000,0000,0000,,or well-designed code. Dialogue: 0,0:01:49.18,0:01:50.93,Default,,0000,0000,0000,,It's not about the actual amount of code you Dialogue: 0,0:01:50.93,0:01:54.64,Default,,0000,0000,0000,,write, but how the code is organized and the Dialogue: 0,0:01:54.64,0:01:59.22,Default,,0000,0000,0000,,size of the units of code. Fundamentally,\Nwriting small Dialogue: 0,0:01:59.22,0:02:01.89,Default,,0000,0000,0000,,code is really design discipline, because\Nthe only way Dialogue: 0,0:02:01.89,0:02:04.73,Default,,0000,0000,0000,,you can write small code is use good design Dialogue: 0,0:02:04.73,0:02:07.57,Default,,0000,0000,0000,,and refactoring. Dialogue: 0,0:02:07.57,0:02:09.84,Default,,0000,0000,0000,,Design and refactoring the way we write small\Ncode. Dialogue: 0,0:02:09.84,0:02:12.25,Default,,0000,0000,0000,,You can't just sit down and write small code, Dialogue: 0,0:02:12.25,0:02:14.75,Default,,0000,0000,0000,,perfectly well-designed code on the first\Ndraft. It doesn't Dialogue: 0,0:02:14.75,0:02:18.45,Default,,0000,0000,0000,,work that way. It's iterative process. Dialogue: 0,0:02:18.45,0:02:23.26,Default,,0000,0000,0000,,So what do I mean by small? It's not Dialogue: 0,0:02:23.26,0:02:27.61,Default,,0000,0000,0000,,about total line count. Well-designed code\Nwill typically have Dialogue: 0,0:02:27.61,0:02:30.75,Default,,0000,0000,0000,,more lines of code than bad code. Just the Dialogue: 0,0:02:30.75,0:02:33.70,Default,,0000,0000,0000,,overhead of declaring methods and classes\Nis gonna increase Dialogue: 0,0:02:33.70,0:02:36.23,Default,,0000,0000,0000,,your line count. Dialogue: 0,0:02:36.23,0:02:39.48,Default,,0000,0000,0000,,It's not about method count. Well-factored\Ncode's gonna have Dialogue: 0,0:02:39.48,0:02:46.48,Default,,0000,0000,0000,,more, smaller methods. It's not about class\Ncount. Well-designed Dialogue: 0,0:02:47.50,0:02:51.24,Default,,0000,0000,0000,,code is almost definitely going to have more\Nclasses Dialogue: 0,0:02:51.24,0:02:53.38,Default,,0000,0000,0000,,than what I call undesigned code. Dialogue: 0,0:02:53.38,0:02:57.53,Default,,0000,0000,0000,,Although I've seen some cases of over-abstraction,\NI find Dialogue: 0,0:02:57.53,0:03:01.39,Default,,0000,0000,0000,,that's pretty rare unless someone goes pattern\Ncrazy. So Dialogue: 0,0:03:01.39,0:03:03.36,Default,,0000,0000,0000,,small code is definitely not about decreasing\Nthe number Dialogue: 0,0:03:03.36,0:03:08.46,Default,,0000,0000,0000,,of classes in your system. It's about well-designed,\Nit's Dialogue: 0,0:03:08.46,0:03:12.34,Default,,0000,0000,0000,,about well-designed classes that aren't poorly\Ndesigned. Dialogue: 0,0:03:12.34,0:03:14.46,Default,,0000,0000,0000,,So what do I mean by small? Small methods, Dialogue: 0,0:03:14.46,0:03:17.29,Default,,0000,0000,0000,,small classes. Small methods are the foundation\Nof writing Dialogue: 0,0:03:17.29,0:03:21.45,Default,,0000,0000,0000,,small code. Without the ability to decompose\Nlarge methods Dialogue: 0,0:03:21.45,0:03:25.18,Default,,0000,0000,0000,,into small methods, we cannot write small\Ncode. And Dialogue: 0,0:03:25.18,0:03:28.18,Default,,0000,0000,0000,,without small methods, we can't raise the\Nlevel of Dialogue: 0,0:03:28.18,0:03:30.74,Default,,0000,0000,0000,,abstraction. Dialogue: 0,0:03:30.74,0:03:32.71,Default,,0000,0000,0000,,To write small code, we have to be able Dialogue: 0,0:03:32.71,0:03:36.35,Default,,0000,0000,0000,,to decompose large classes into smaller classes,\Nand abstract Dialogue: 0,0:03:36.35,0:03:40.40,Default,,0000,0000,0000,,responsibilities out of them and separate\Nthem on higher-level, Dialogue: 0,0:03:40.40,0:03:44.18,Default,,0000,0000,0000,,and base them on higher-level abstractions. Dialogue: 0,0:03:44.18,0:03:46.46,Default,,0000,0000,0000,,It's important that our classes are small,\Nbecause small Dialogue: 0,0:03:46.46,0:03:52.53,Default,,0000,0000,0000,,classes are what lead to reusability and composability. Dialogue: 0,0:03:52.53,0:03:59.53,Default,,0000,0000,0000,,So, why should we strive for small code? Why Dialogue: 0,0:04:00.64,0:04:05.02,Default,,0000,0000,0000,,is it important? Dialogue: 0,0:04:05.02,0:04:08.88,Default,,0000,0000,0000,,We don't know what the future is going to Dialogue: 0,0:04:08.88,0:04:15.68,Default,,0000,0000,0000,,bring. Your software requirements are going\Nto change. Software Dialogue: 0,0:04:15.68,0:04:19.34,Default,,0000,0000,0000,,must be amenable to change. Any system of\Nsoftware Dialogue: 0,0:04:19.34,0:04:23.37,Default,,0000,0000,0000,,that's going to have a long, successful life,\Nis Dialogue: 0,0:04:23.37,0:04:28.25,Default,,0000,0000,0000,,going to change significantly. Small code\Nis simply easier Dialogue: 0,0:04:28.25,0:04:32.16,Default,,0000,0000,0000,,to work with than large, complex code. If\Nthe Dialogue: 0,0:04:32.16,0:04:34.99,Default,,0000,0000,0000,,requirements of your software are never gonna\Nchange, you Dialogue: 0,0:04:34.99,0:04:38.09,Default,,0000,0000,0000,,can ignore everything that I have to say here. Dialogue: 0,0:04:38.09,0:04:45.09,Default,,0000,0000,0000,,But I doubt that that's the case. Dialogue: 0,0:04:45.41,0:04:47.73,Default,,0000,0000,0000,,We should write small code because it helps\Nus Dialogue: 0,0:04:47.73,0:04:52.20,Default,,0000,0000,0000,,raise the level of abstraction in our code.\NIt's Dialogue: 0,0:04:52.20,0:04:54.03,Default,,0000,0000,0000,,one of the most important things we do to Dialogue: 0,0:04:54.03,0:05:00.29,Default,,0000,0000,0000,,create readable, understandable code. All\Ngood design is really Dialogue: 0,0:05:00.29,0:05:05.34,Default,,0000,0000,0000,,driving toward expressing ubiquitous language\Nof our problem domain Dialogue: 0,0:05:05.34,0:05:07.96,Default,,0000,0000,0000,,in our code. Dialogue: 0,0:05:07.96,0:05:10.88,Default,,0000,0000,0000,,The combination of small methods and small\Nclasses is Dialogue: 0,0:05:10.88,0:05:13.87,Default,,0000,0000,0000,,going to help us raise that level of abstraction Dialogue: 0,0:05:13.87,0:05:20.87,Default,,0000,0000,0000,,and express those higher-level domain concepts. Dialogue: 0,0:05:21.23,0:05:22.87,Default,,0000,0000,0000,,We should also write small code so we can Dialogue: 0,0:05:22.87,0:05:27.96,Default,,0000,0000,0000,,effectively use composition. Small classes\Nand small methods compose Dialogue: 0,0:05:27.96,0:05:31.82,Default,,0000,0000,0000,,together well. As we compose instances of\Nsmall objects Dialogue: 0,0:05:31.82,0:05:36.93,Default,,0000,0000,0000,,together, our systems will become message-based.\NIn order to Dialogue: 0,0:05:36.93,0:05:39.91,Default,,0000,0000,0000,,build systems that are message-based, we have\Nto use Dialogue: 0,0:05:39.91,0:05:44.57,Default,,0000,0000,0000,,delegation. And small, composable parts. Small\Ncode makes small Dialogue: 0,0:05:44.57,0:05:47.78,Default,,0000,0000,0000,,composable parts. It's gonna help our software\Nhave flexibility Dialogue: 0,0:05:47.78,0:05:50.11,Default,,0000,0000,0000,,and lead to a suppleness over time, and allow Dialogue: 0,0:05:50.11,0:05:52.90,Default,,0000,0000,0000,,us to follow those messages. And eventually\Nwe're gonna Dialogue: 0,0:05:52.90,0:05:57.70,Default,,0000,0000,0000,,see, find our duck types. Dialogue: 0,0:05:57.70,0:06:01.43,Default,,0000,0000,0000,,And all this is about enabling future change.\NAnd Dialogue: 0,0:06:01.43,0:06:06.51,Default,,0000,0000,0000,,accommodate the future requirements without\Na forklift replacement. Dialogue: 0,0:06:06.51,0:06:12.49,Default,,0000,0000,0000,,So the goal: small units of understandable\Ncode that Dialogue: 0,0:06:12.49,0:06:18.10,Default,,0000,0000,0000,,are amenable to change. Dialogue: 0,0:06:18.10,0:06:22.30,Default,,0000,0000,0000,,Our primary tools are extract method and extract\Nclass. Dialogue: 0,0:06:22.30,0:06:25.35,Default,,0000,0000,0000,,Longer methods are harder to understand than\Nshort methods. Dialogue: 0,0:06:25.35,0:06:26.72,Default,,0000,0000,0000,,And most of the time, we can shorten a Dialogue: 0,0:06:26.72,0:06:30.87,Default,,0000,0000,0000,,method simply by using the abstract method\Nrefactoring. I Dialogue: 0,0:06:30.87,0:06:35.07,Default,,0000,0000,0000,,use this thing all the time when I'm coding. Dialogue: 0,0:06:35.07,0:06:36.24,Default,,0000,0000,0000,,And once we have a set of methods that Dialogue: 0,0:06:36.24,0:06:39.68,Default,,0000,0000,0000,,are coherent around a concept, then we can\Nlook Dialogue: 0,0:06:39.68,0:06:43.06,Default,,0000,0000,0000,,to abstract those into a separate class and\Nmove Dialogue: 0,0:06:43.06,0:06:47.29,Default,,0000,0000,0000,,the methods to that new class. Dialogue: 0,0:06:47.29,0:06:50.13,Default,,0000,0000,0000,,So, I'm gonna be using the example of a Dialogue: 0,0:06:50.13,0:06:53.68,Default,,0000,0000,0000,,command line option parser that handles booleans\Nto start Dialogue: 0,0:06:53.68,0:06:55.65,Default,,0000,0000,0000,,with, and then we're gonna see where the future Dialogue: 0,0:06:55.65,0:06:57.02,Default,,0000,0000,0000,,takes us. Dialogue: 0,0:06:57.02,0:07:01.77,Default,,0000,0000,0000,,So, with the command line, I want to be Dialogue: 0,0:07:01.77,0:07:05.75,Default,,0000,0000,0000,,able to run some Ruby program dash v. And Dialogue: 0,0:07:05.75,0:07:11.58,Default,,0000,0000,0000,,handle boolean options. That's where we're\Ngonna start. Dialogue: 0,0:07:11.58,0:07:13.55,Default,,0000,0000,0000,,In my Ruby program, I want to define what Dialogue: 0,0:07:13.55,0:07:20.55,Default,,0000,0000,0000,,options I'm looking for, using this simple\NDSL. And Dialogue: 0,0:07:21.33,0:07:22.67,Default,,0000,0000,0000,,then I want to be able to consume it Dialogue: 0,0:07:22.67,0:07:26.84,Default,,0000,0000,0000,,like this. If options.has and then a particular\Noption, Dialogue: 0,0:07:26.84,0:07:28.18,Default,,0000,0000,0000,,I do something. Dialogue: 0,0:07:28.18,0:07:34.50,Default,,0000,0000,0000,,Putting it all together. The DSL, the program\Nat Dialogue: 0,0:07:34.50,0:07:36.39,Default,,0000,0000,0000,,the top, the DSL, and then how we actually Dialogue: 0,0:07:36.39,0:07:42.75,Default,,0000,0000,0000,,consume that options object. Pretty simple. Dialogue: 0,0:07:42.75,0:07:47.94,Default,,0000,0000,0000,,Here's my spec. It's pretty simple. It's true\Nif Dialogue: 0,0:07:47.94,0:07:49.90,Default,,0000,0000,0000,,the option is defined and it's present on\Nthe Dialogue: 0,0:07:49.90,0:07:54.30,Default,,0000,0000,0000,,command line. And it's false if it's not. Dialogue: 0,0:07:54.30,0:07:56.47,Default,,0000,0000,0000,,So I run my specs and I get two Dialogue: 0,0:07:56.47,0:08:03.47,Default,,0000,0000,0000,,failures. Yes, I used TDD. So, here's my implementation Dialogue: 0,0:08:04.72,0:08:09.44,Default,,0000,0000,0000,,that fits on one slide. Pretty simply, I store Dialogue: 0,0:08:09.44,0:08:12.55,Default,,0000,0000,0000,,the defined options in an array, and I store Dialogue: 0,0:08:12.55,0:08:17.34,Default,,0000,0000,0000,,the arguments, the argv for later reference.\NThen I Dialogue: 0,0:08:17.34,0:08:18.80,Default,,0000,0000,0000,,have a has method that checks to see if Dialogue: 0,0:08:18.80,0:08:20.86,Default,,0000,0000,0000,,the option is defined. If it's present in\Nthe Dialogue: 0,0:08:20.86,0:08:21.63,Default,,0000,0000,0000,,argv. Dialogue: 0,0:08:21.63,0:08:25.27,Default,,0000,0000,0000,,And then I've got my option method, which\Nimplements Dialogue: 0,0:08:25.27,0:08:30.77,Default,,0000,0000,0000,,my simple DSL. Nice and readable. Fits on\None Dialogue: 0,0:08:30.77,0:08:35.44,Default,,0000,0000,0000,,slide. Probably very comprehendable. Dialogue: 0,0:08:35.44,0:08:40.36,Default,,0000,0000,0000,,So I run my tests. Zero failures. They pass. Dialogue: 0,0:08:40.36,0:08:45.65,Default,,0000,0000,0000,,I'm done. I get to go home until the Dialogue: 0,0:08:45.65,0:08:48.66,Default,,0000,0000,0000,,future comes along. And my workmate comes\Nalong and Dialogue: 0,0:08:48.66,0:08:51.50,Default,,0000,0000,0000,,says, hey, I really like that library. But,\Ncould Dialogue: 0,0:08:51.50,0:08:54.50,Default,,0000,0000,0000,,we handle string options? Dialogue: 0,0:08:54.50,0:08:59.61,Default,,0000,0000,0000,,Sounds pretty simple. Pretty straightforward.\NSo I think about Dialogue: 0,0:08:59.61,0:09:01.11,Default,,0000,0000,0000,,that, and I come up with a small extension Dialogue: 0,0:09:01.11,0:09:04.11,Default,,0000,0000,0000,,to the DSL, to just pass a second argument Dialogue: 0,0:09:04.11,0:09:06.30,Default,,0000,0000,0000,,as an option with a symbol representation\Nof the Dialogue: 0,0:09:06.30,0:09:08.02,Default,,0000,0000,0000,,option type. String, in this case. Dialogue: 0,0:09:08.02,0:09:10.19,Default,,0000,0000,0000,,I also default to being a boolean so I Dialogue: 0,0:09:10.19,0:09:12.11,Default,,0000,0000,0000,,don't have to change the code that other people Dialogue: 0,0:09:12.11,0:09:15.15,Default,,0000,0000,0000,,have done. Dialogue: 0,0:09:15.15,0:09:18.64,Default,,0000,0000,0000,,So, a string option. It's a little different\Nthan Dialogue: 0,0:09:18.64,0:09:23.31,Default,,0000,0000,0000,,a boolean. It actually requires content. So\Nnow I Dialogue: 0,0:09:23.31,0:09:26.52,Default,,0000,0000,0000,,need the concept of validation. If the string\Noption Dialogue: 0,0:09:26.52,0:09:28.43,Default,,0000,0000,0000,,is missing the content, it's not valid. There's\Nno Dialogue: 0,0:09:28.43,0:09:30.59,Default,,0000,0000,0000,,string there. Dialogue: 0,0:09:30.59,0:09:33.43,Default,,0000,0000,0000,,So, then I'm gonna normalize how I get the Dialogue: 0,0:09:33.43,0:09:35.52,Default,,0000,0000,0000,,values out of both those string options and\Nthose Dialogue: 0,0:09:35.52,0:09:38.30,Default,,0000,0000,0000,,boolean options. You know, that value. This\Nis gonna Dialogue: 0,0:09:38.30,0:09:40.88,Default,,0000,0000,0000,,change the API, but sometimes you actually\Nneed to Dialogue: 0,0:09:40.88,0:09:43.75,Default,,0000,0000,0000,,break the API to enable the future. Dialogue: 0,0:09:43.75,0:09:46.69,Default,,0000,0000,0000,,And I'm doing it pretty early. I've only got Dialogue: 0,0:09:46.69,0:09:50.97,Default,,0000,0000,0000,,one guy in my office using the library at Dialogue: 0,0:09:50.97,0:09:53.21,Default,,0000,0000,0000,,the moment. So, again, putting it all together.\NI Dialogue: 0,0:09:53.21,0:09:55.33,Default,,0000,0000,0000,,can pass the options on the command line.\NI Dialogue: 0,0:09:55.33,0:09:58.05,Default,,0000,0000,0000,,define the options with the DSL, and here's\Nhow Dialogue: 0,0:09:58.05,0:10:00.36,Default,,0000,0000,0000,,I use my valid? and my value methods to Dialogue: 0,0:10:00.36,0:10:02.68,Default,,0000,0000,0000,,find out, get, find out if it's valued and Dialogue: 0,0:10:02.68,0:10:05.64,Default,,0000,0000,0000,,get my values out. Dialogue: 0,0:10:05.64,0:10:09.96,Default,,0000,0000,0000,,So, now here's the class that implements it.\NAgain, Dialogue: 0,0:10:09.96,0:10:12.95,Default,,0000,0000,0000,,on one slide. Probably not as readable. Probably\Nnot Dialogue: 0,0:10:12.95,0:10:16.40,Default,,0000,0000,0000,,as comprehensible. We're going down what I\Ncall kind Dialogue: 0,0:10:16.40,0:10:20.24,Default,,0000,0000,0000,,of the undesigned path. It's not too big.\NThirty-one Dialogue: 0,0:10:20.24,0:10:22.09,Default,,0000,0000,0000,,lines. But it's got issues. Dialogue: 0,0:10:22.09,0:10:24.39,Default,,0000,0000,0000,,It's got a method that's definitely large.\NOne that's Dialogue: 0,0:10:24.39,0:10:27.80,Default,,0000,0000,0000,,looking on the verge of being large. It's\Ngot, Dialogue: 0,0:10:27.80,0:10:29.98,Default,,0000,0000,0000,,for only handling booleans and strings, it\Nhas quite Dialogue: 0,0:10:29.98,0:10:34.30,Default,,0000,0000,0000,,a bit of, of conditional complexity in it\Nalready. Dialogue: 0,0:10:34.30,0:10:36.11,Default,,0000,0000,0000,,And as we're soon gonna see, it's not very Dialogue: 0,0:10:36.11,0:10:37.69,Default,,0000,0000,0000,,amenable to change. Dialogue: 0,0:10:37.69,0:10:39.64,Default,,0000,0000,0000,,So we'll look at the pieces and how they Dialogue: 0,0:10:39.64,0:10:40.85,Default,,0000,0000,0000,,work, just so yo understand it. Dialogue: 0,0:10:40.85,0:10:42.84,Default,,0000,0000,0000,,That's my initialize method. It creates a\Nhash to Dialogue: 0,0:10:42.84,0:10:44.82,Default,,0000,0000,0000,,store the options. Because we have to store\Nthe Dialogue: 0,0:10:44.82,0:10:47.43,Default,,0000,0000,0000,,type now, not just that we have an option. Dialogue: 0,0:10:47.43,0:10:50.50,Default,,0000,0000,0000,,It's either boolean or string. And the rest\Nof Dialogue: 0,0:10:50.50,0:10:54.56,Default,,0000,0000,0000,,the initialization is the same as it was before. Dialogue: 0,0:10:54.56,0:10:57.26,Default,,0000,0000,0000,,And the valid method, we gotta iterate over\Nthe Dialogue: 0,0:10:57.26,0:10:59.95,Default,,0000,0000,0000,,options, looking to see which ones are strings.\NSo Dialogue: 0,0:10:59.95,0:11:03.42,Default,,0000,0000,0000,,we're doing checking on type here. And, and\Ntrying Dialogue: 0,0:11:03.42,0:11:06.19,Default,,0000,0000,0000,,to see whether they're present and they actually\Nhave Dialogue: 0,0:11:06.19,0:11:06.59,Default,,0000,0000,0000,,content. Dialogue: 0,0:11:06.59,0:11:08.72,Default,,0000,0000,0000,,Currently, string options are the only ones\Nthat need Dialogue: 0,0:11:08.72,0:11:12.61,Default,,0000,0000,0000,,to validate. Boolean options, there's nothing\Nreally to validate. Dialogue: 0,0:11:12.61,0:11:14.68,Default,,0000,0000,0000,,Either it's there or it's not. No validation.\NBut Dialogue: 0,0:11:14.68,0:11:15.92,Default,,0000,0000,0000,,strings, we have to. Dialogue: 0,0:11:15.92,0:11:18.21,Default,,0000,0000,0000,,And the value method, it does a lot of Dialogue: 0,0:11:18.21,0:11:21.41,Default,,0000,0000,0000,,stuff. Let's just pretend for a moment this\Nmethod Dialogue: 0,0:11:21.41,0:11:23.10,Default,,0000,0000,0000,,is a black box. We're gonna come back to Dialogue: 0,0:11:23.10,0:11:26.09,Default,,0000,0000,0000,,it later. Cause this is, by far, the worst Dialogue: 0,0:11:26.09,0:11:29.28,Default,,0000,0000,0000,,code in this current example. Dialogue: 0,0:11:29.28,0:11:34.30,Default,,0000,0000,0000,,But, everything is spec'd. And all my specs\Nare Dialogue: 0,0:11:34.30,0:11:36.40,Default,,0000,0000,0000,,green. Dialogue: 0,0:11:36.40,0:11:40.12,Default,,0000,0000,0000,,So let's talk about methods. Cause we've got\Nsome Dialogue: 0,0:11:40.12,0:11:42.42,Default,,0000,0000,0000,,big ones and we need to clean them up. Dialogue: 0,0:11:42.42,0:11:47.63,Default,,0000,0000,0000,,I call it the first rule of method, of Dialogue: 0,0:11:47.63,0:11:51.59,Default,,0000,0000,0000,,methods. Do one thing. Do it well. Do only Dialogue: 0,0:11:51.59,0:11:52.48,Default,,0000,0000,0000,,one thing. Dialogue: 0,0:11:52.48,0:11:55.44,Default,,0000,0000,0000,,Harkens back to that Unix philosophy of tools\Nthat Dialogue: 0,0:11:55.44,0:11:59.29,Default,,0000,0000,0000,,you string together with standard in, standard\Nout. But Dialogue: 0,0:11:59.29,0:12:01.15,Default,,0000,0000,0000,,how do we determine if a method is actually Dialogue: 0,0:12:01.15,0:12:04.61,Default,,0000,0000,0000,,only doing one thing? This is where your level Dialogue: 0,0:12:04.61,0:12:08.00,Default,,0000,0000,0000,,of abstraction and the abstract, abstractions\Nin your code Dialogue: 0,0:12:08.00,0:12:10.58,Default,,0000,0000,0000,,come into play. And you need to develop a Dialogue: 0,0:12:10.58,0:12:13.40,Default,,0000,0000,0000,,feel for this over time. That you want one Dialogue: 0,0:12:13.40,0:12:16.13,Default,,0000,0000,0000,,level of abstraction per method. Dialogue: 0,0:12:16.13,0:12:18.63,Default,,0000,0000,0000,,If all of our statements are the same level Dialogue: 0,0:12:18.63,0:12:22.53,Default,,0000,0000,0000,,of abstraction, and they're coherent around\Na purpose, then Dialogue: 0,0:12:22.53,0:12:25.81,Default,,0000,0000,0000,,I consider that to be doing one thing. Doesn't Dialogue: 0,0:12:25.81,0:12:27.62,Default,,0000,0000,0000,,mean it has to be one line in a Dialogue: 0,0:12:27.62,0:12:30.22,Default,,0000,0000,0000,,method. Dialogue: 0,0:12:30.22,0:12:32.40,Default,,0000,0000,0000,,I can't tell you how many times I've looked Dialogue: 0,0:12:32.40,0:12:36.01,Default,,0000,0000,0000,,at code and seen a comment on a method Dialogue: 0,0:12:36.01,0:12:37.73,Default,,0000,0000,0000,,that was, like, an excellent description of\Nwhat the Dialogue: 0,0:12:37.73,0:12:39.84,Default,,0000,0000,0000,,method did, and if you just took those words, Dialogue: 0,0:12:39.84,0:12:42.16,Default,,0000,0000,0000,,bound together, they'd make a fantastic method\Nname. But Dialogue: 0,0:12:42.16,0:12:45.31,Default,,0000,0000,0000,,yet the method is named something else that\Nisn't Dialogue: 0,0:12:45.31,0:12:50.98,Default,,0000,0000,0000,,that descriptive. So use descriptive names.\NIt's really critical. Dialogue: 0,0:12:50.98,0:12:53.15,Default,,0000,0000,0000,,And the fewer arguments, the better. My personal\Ngoal Dialogue: 0,0:12:53.15,0:12:57.67,Default,,0000,0000,0000,,is zero arguments on methods. One is OK. Two Dialogue: 0,0:12:57.67,0:12:59.79,Default,,0000,0000,0000,,or three. That's when I start to think I've Dialogue: 0,0:12:59.79,0:13:02.59,Default,,0000,0000,0000,,probably missed an abstraction in my code\Nand I Dialogue: 0,0:13:02.59,0:13:06.70,Default,,0000,0000,0000,,should go back and look at it. Dialogue: 0,0:13:06.70,0:13:09.82,Default,,0000,0000,0000,,Separate queries from commands. If you query\Nsomething and Dialogue: 0,0:13:09.82,0:13:11.52,Default,,0000,0000,0000,,it looks like a query method and it changes Dialogue: 0,0:13:11.52,0:13:14.17,Default,,0000,0000,0000,,the state of your object, it's hard to reason Dialogue: 0,0:13:14.17,0:13:16.60,Default,,0000,0000,0000,,about, and people who consume your library\Nwill be Dialogue: 0,0:13:16.60,0:13:20.57,Default,,0000,0000,0000,,confused by that. So separate those. Dialogue: 0,0:13:20.57,0:13:24.59,Default,,0000,0000,0000,,And, don't repeat yourself. I know Sandi talked\Nabout Dialogue: 0,0:13:24.59,0:13:27.81,Default,,0000,0000,0000,,this earlier, and it does take some judgment\Nto Dialogue: 0,0:13:27.81,0:13:31.09,Default,,0000,0000,0000,,know when it is time to remove the repetition. Dialogue: 0,0:13:31.09,0:13:32.95,Default,,0000,0000,0000,,But you don't want to leave repetition over\Nthe Dialogue: 0,0:13:32.95,0:13:35.75,Default,,0000,0000,0000,,long term, because it will come back to bite Dialogue: 0,0:13:35.75,0:13:37.38,Default,,0000,0000,0000,,you. Dialogue: 0,0:13:37.38,0:13:42.01,Default,,0000,0000,0000,,So, let's look at our methods. We've got repetition Dialogue: 0,0:13:42.01,0:13:44.07,Default,,0000,0000,0000,,here. Both valid? and value are digging through\Nthe Dialogue: 0,0:13:44.07,0:13:46.66,Default,,0000,0000,0000,,argv array to find the options from the command Dialogue: 0,0:13:46.66,0:13:49.64,Default,,0000,0000,0000,,line. This is the perfect candidate for an\Nextract Dialogue: 0,0:13:49.64,0:13:53.78,Default,,0000,0000,0000,,method, abstraction. Refactoring. Dialogue: 0,0:13:53.78,0:13:56.91,Default,,0000,0000,0000,,We have magic constants scattered around,\Nand those are Dialogue: 0,0:13:56.91,0:14:02.12,Default,,0000,0000,0000,,a strong indication that we've missed something.\NAn abstraction. Dialogue: 0,0:14:02.12,0:14:06.14,Default,,0000,0000,0000,,We're violating some other rules. It's hard\Nto say Dialogue: 0,0:14:06.14,0:14:11.15,Default,,0000,0000,0000,,either of these methods is really doing one\Nthing. Dialogue: 0,0:14:11.15,0:14:13.83,Default,,0000,0000,0000,,The code is definitely not at the same level Dialogue: 0,0:14:13.83,0:14:17.93,Default,,0000,0000,0000,,of abstraction. Values digging, valid? is\Ndigging into the Dialogue: 0,0:14:17.93,0:14:20.76,Default,,0000,0000,0000,,argv array and value is figuring out different\Ndivergent Dialogue: 0,0:14:20.76,0:14:23.67,Default,,0000,0000,0000,,types and how to return their values. So we're Dialogue: 0,0:14:23.67,0:14:25.80,Default,,0000,0000,0000,,gonna eliminate some of the repetition with\Nthe extract Dialogue: 0,0:14:25.80,0:14:28.63,Default,,0000,0000,0000,,method refactoring. Dialogue: 0,0:14:28.63,0:14:33.16,Default,,0000,0000,0000,,The extract method refactoring entails moving\Na part of Dialogue: 0,0:14:33.16,0:14:35.42,Default,,0000,0000,0000,,a method into a new method, with a descriptive Dialogue: 0,0:14:35.42,0:14:38.45,Default,,0000,0000,0000,,name, that's the naming part. And then calling\Nthe Dialogue: 0,0:14:38.45,0:14:39.53,Default,,0000,0000,0000,,new method. Dialogue: 0,0:14:39.53,0:14:42.31,Default,,0000,0000,0000,,This, this refactoring helps us keep the level\Nof Dialogue: 0,0:14:42.31,0:14:47.82,Default,,0000,0000,0000,,abstraction consistent in the method we're\Nabstracting from. Here Dialogue: 0,0:14:47.82,0:14:49.46,Default,,0000,0000,0000,,we have one expression on a method that's\Na Dialogue: 0,0:14:49.46,0:14:52.84,Default,,0000,0000,0000,,high level of abstraction, and two statements\Nthat are Dialogue: 0,0:14:52.84,0:14:56.53,Default,,0000,0000,0000,,a low level of abstraction. So we move the Dialogue: 0,0:14:56.53,0:14:58.51,Default,,0000,0000,0000,,less abstract code to a new method with a Dialogue: 0,0:14:58.51,0:15:01.93,Default,,0000,0000,0000,,descriptive name, and then we call the new\Nmethod. Dialogue: 0,0:15:01.93,0:15:04.92,Default,,0000,0000,0000,,And this results in the old method, method\Nhaving Dialogue: 0,0:15:04.92,0:15:09.99,Default,,0000,0000,0000,,a consistent level of abstraction. So back\Nto our Dialogue: 0,0:15:09.99,0:15:14.16,Default,,0000,0000,0000,,CommandLineOptions class, both valid? and\Nvalue are digging through Dialogue: 0,0:15:14.16,0:15:16.69,Default,,0000,0000,0000,,the argv collection to find the option value.\NSo Dialogue: 0,0:15:16.69,0:15:18.74,Default,,0000,0000,0000,,we're gonna abstract that code and get the\Nraw Dialogue: 0,0:15:18.74,0:15:22.11,Default,,0000,0000,0000,,value out of argv. Dialogue: 0,0:15:22.11,0:15:23.75,Default,,0000,0000,0000,,Then we call the method from where the original Dialogue: 0,0:15:23.75,0:15:27.52,Default,,0000,0000,0000,,logic was abstracted. Pretty simple. But now\Nthe code Dialogue: 0,0:15:27.52,0:15:30.09,Default,,0000,0000,0000,,left behind in valid? and value says what\NI Dialogue: 0,0:15:30.09,0:15:34.44,Default,,0000,0000,0000,,want. Not how to do it. Dialogue: 0,0:15:34.44,0:15:37.76,Default,,0000,0000,0000,,The how has been moved to the abstracted method, Dialogue: 0,0:15:37.76,0:15:40.47,Default,,0000,0000,0000,,raising the level of abstraction just a little\Nbit Dialogue: 0,0:15:40.47,0:15:44.24,Default,,0000,0000,0000,,in valid? and value. Dialogue: 0,0:15:44.24,0:15:47.28,Default,,0000,0000,0000,,I'm going to do two more abstractions. I've\Nabstracted Dialogue: 0,0:15:47.28,0:15:51.54,Default,,0000,0000,0000,,the string option value method and the abstract\Ncontent Dialogue: 0,0:15:51.54,0:15:55.39,Default,,0000,0000,0000,,method. The naming of the abstracted methods\Nis very Dialogue: 0,0:15:55.39,0:16:00.54,Default,,0000,0000,0000,,important. They say what they do. Dialogue: 0,0:16:00.54,0:16:03.34,Default,,0000,0000,0000,,But overall, I'm not happy with this code.\NIt Dialogue: 0,0:16:03.34,0:16:06.33,Default,,0000,0000,0000,,is more explanatory, but it's fairly complex\Nand hard Dialogue: 0,0:16:06.33,0:16:09.81,Default,,0000,0000,0000,,to understand. It's also not as small as it Dialogue: 0,0:16:09.81,0:16:12.63,Default,,0000,0000,0000,,could be. The methods are large because I\Nmissed Dialogue: 0,0:16:12.63,0:16:17.50,Default,,0000,0000,0000,,an abstraction. And we're gonna go find that\Nnow. Dialogue: 0,0:16:17.50,0:16:19.73,Default,,0000,0000,0000,,I'm referencing the option type symbol to\Nsee if Dialogue: 0,0:16:19.73,0:16:24.52,Default,,0000,0000,0000,,it's a string, which, that's a big smell.\NThen Dialogue: 0,0:16:24.52,0:16:26.29,Default,,0000,0000,0000,,there are the magic constants used to dig\Ninto Dialogue: 0,0:16:26.29,0:16:28.96,Default,,0000,0000,0000,,the argv element to find the constant within\Nthat Dialogue: 0,0:16:28.96,0:16:34.24,Default,,0000,0000,0000,,particular string, the substring. If I was\Nconfident that Dialogue: 0,0:16:34.24,0:16:36.71,Default,,0000,0000,0000,,I'd have no future added requirements for\Nthis class, Dialogue: 0,0:16:36.71,0:16:40.64,Default,,0000,0000,0000,,I might leave this alone. It works. It's tested. Dialogue: 0,0:16:40.64,0:16:43.22,Default,,0000,0000,0000,,Until my buddy comes to me and says, hey, Dialogue: 0,0:16:43.22,0:16:45.30,Default,,0000,0000,0000,,I really like that library, but could we handle Dialogue: 0,0:16:45.30,0:16:48.15,Default,,0000,0000,0000,,integers now? Dialogue: 0,0:16:48.15,0:16:50.96,Default,,0000,0000,0000,,I could keep driving down this undesigned\Npath I've Dialogue: 0,0:16:50.96,0:16:53.91,Default,,0000,0000,0000,,been following, and complicate the valid?\Nand value methods Dialogue: 0,0:16:53.91,0:16:56.42,Default,,0000,0000,0000,,by switching on the type of the option and Dialogue: 0,0:16:56.42,0:16:59.79,Default,,0000,0000,0000,,digging into those argv elements to find the\Nvalue. Dialogue: 0,0:16:59.79,0:17:03.69,Default,,0000,0000,0000,,But, this is our chance to make a break. Dialogue: 0,0:17:03.69,0:17:07.38,Default,,0000,0000,0000,,And make our code more amenable to change. Dialogue: 0,0:17:07.38,0:17:11.33,Default,,0000,0000,0000,,But, to illustrate the point, I'm gonna show\Nyou Dialogue: 0,0:17:11.33,0:17:14.94,Default,,0000,0000,0000,,that undesign method, to show you the OO design Dialogue: 0,0:17:14.94,0:17:18.20,Default,,0000,0000,0000,,actually matters. So we're gonna look at this. Dialogue: 0,0:17:18.20,0:17:20.40,Default,,0000,0000,0000,,This is the undesigned, non OO version of\Nthis Dialogue: 0,0:17:20.40,0:17:23.87,Default,,0000,0000,0000,,code. Is it horrible? I'll leave that to you Dialogue: 0,0:17:23.87,0:17:27.12,Default,,0000,0000,0000,,to decide. Is it small? In my opinion, definitely Dialogue: 0,0:17:27.12,0:17:30.12,Default,,0000,0000,0000,,not. It is not small, by any measure. The Dialogue: 0,0:17:30.12,0:17:32.09,Default,,0000,0000,0000,,class is growing due to changes in specification.\NThe Dialogue: 0,0:17:32.09,0:17:36.49,Default,,0000,0000,0000,,valid? and value methods are being changed\Nin lock Dialogue: 0,0:17:36.49,0:17:39.17,Default,,0000,0000,0000,,step. That's a sure sign we've missed an abstraction Dialogue: 0,0:17:39.17,0:17:41.85,Default,,0000,0000,0000,,or a duck type. And those methods are getting Dialogue: 0,0:17:41.85,0:17:45.90,Default,,0000,0000,0000,,big and complicated. And now they're doing\Neven more Dialogue: 0,0:17:45.90,0:17:47.34,Default,,0000,0000,0000,,things. Dialogue: 0,0:17:47.34,0:17:52.38,Default,,0000,0000,0000,,And we're just doing booleans, strings, and\Nintegers. Not Dialogue: 0,0:17:52.38,0:17:53.34,Default,,0000,0000,0000,,that much. Dialogue: 0,0:17:53.34,0:18:00.27,Default,,0000,0000,0000,,The code has tests. They all pass. That's\Ngood. Dialogue: 0,0:18:00.27,0:18:03.67,Default,,0000,0000,0000,,But it's not satisfying. We've got those large\Nmethods Dialogue: 0,0:18:03.67,0:18:07.88,Default,,0000,0000,0000,,and complex conditional logic. It's time to\Nrefactor now. Dialogue: 0,0:18:07.88,0:18:10.57,Default,,0000,0000,0000,,To make the change easy. Dialogue: 0,0:18:10.57,0:18:12.34,Default,,0000,0000,0000,,And now we've got the tests that are back, Dialogue: 0,0:18:12.34,0:18:14.68,Default,,0000,0000,0000,,so we can do it without fear. Dialogue: 0,0:18:14.68,0:18:18.42,Default,,0000,0000,0000,,And, I want to call your attention to a Dialogue: 0,0:18:18.42,0:18:21.74,Default,,0000,0000,0000,,pattern that clearly emerges when we go down\Nthe Dialogue: 0,0:18:21.74,0:18:25.72,Default,,0000,0000,0000,,non OO path here. We see checking the option Dialogue: 0,0:18:25.72,0:18:30.94,Default,,0000,0000,0000,,type and divergent behavior based on the type.\NDon't Dialogue: 0,0:18:30.94,0:18:36.27,Default,,0000,0000,0000,,reinvent the type system. If you have ducks,\Nlet Dialogue: 0,0:18:36.27,0:18:37.93,Default,,0000,0000,0000,,them quack. Dialogue: 0,0:18:37.93,0:18:40.71,Default,,0000,0000,0000,,In this example, the option types of boolean,\Nstring, Dialogue: 0,0:18:40.71,0:18:44.08,Default,,0000,0000,0000,,and integer, those are our ducks. And I'll\Nbet Dialogue: 0,0:18:44.08,0:18:47.83,Default,,0000,0000,0000,,there's ducks in your code yearning to be\Nfree. Dialogue: 0,0:18:47.83,0:18:51.41,Default,,0000,0000,0000,,And just a further confirmation that we're\Ndealing with Dialogue: 0,0:18:51.41,0:18:55.30,Default,,0000,0000,0000,,an abstraction or a duck, we see the testing Dialogue: 0,0:18:55.30,0:18:59.19,Default,,0000,0000,0000,,option type again in the value method. Hidden\Ninside Dialogue: 0,0:18:59.19,0:19:02.13,Default,,0000,0000,0000,,the valid? and value method, there's a case\Nstatement Dialogue: 0,0:19:02.13,0:19:04.70,Default,,0000,0000,0000,,here. It just didn't evolve that way as I Dialogue: 0,0:19:04.70,0:19:05.56,Default,,0000,0000,0000,,was writing the code. Dialogue: 0,0:19:05.56,0:19:08.72,Default,,0000,0000,0000,,I'm gonna show you that. You're gonna see\Nthat Dialogue: 0,0:19:08.72,0:19:12.84,Default,,0000,0000,0000,,it's really clear now. Dialogue: 0,0:19:12.84,0:19:14.65,Default,,0000,0000,0000,,Now it should be really obvious what the duck Dialogue: 0,0:19:14.65,0:19:17.28,Default,,0000,0000,0000,,type is. If you have case statements like\Nthis Dialogue: 0,0:19:17.28,0:19:24.28,Default,,0000,0000,0000,,in your code, you've missed an abstraction.\NHere, again, Dialogue: 0,0:19:24.54,0:19:26.83,Default,,0000,0000,0000,,we clearly see the duck type. Dialogue: 0,0:19:26.83,0:19:30.07,Default,,0000,0000,0000,,Now, I would guess, if I was writing this, Dialogue: 0,0:19:30.07,0:19:32.42,Default,,0000,0000,0000,,as soon as I had the string type, I Dialogue: 0,0:19:32.42,0:19:35.36,Default,,0000,0000,0000,,would have gone down the OO path. I just Dialogue: 0,0:19:35.36,0:19:38.69,Default,,0000,0000,0000,,wanted to illustrate to you what an undesigned,\Nnon Dialogue: 0,0:19:38.69,0:19:41.70,Default,,0000,0000,0000,,OO mess you can get yourself into if you Dialogue: 0,0:19:41.70,0:19:45.57,Default,,0000,0000,0000,,keep riding the horse until it's dead. Dialogue: 0,0:19:45.57,0:19:47.29,Default,,0000,0000,0000,,My dad had a saying hanging on his wall Dialogue: 0,0:19:47.29,0:19:49.93,Default,,0000,0000,0000,,in his office. When the horse is dead, get Dialogue: 0,0:19:49.93,0:19:52.06,Default,,0000,0000,0000,,off. Dialogue: 0,0:19:52.06,0:19:54.81,Default,,0000,0000,0000,,But sometimes we don't realize the horse is\Ndead Dialogue: 0,0:19:54.81,0:19:58.16,Default,,0000,0000,0000,,and we just keep trying to go. Now it's Dialogue: 0,0:19:58.16,0:20:01.44,Default,,0000,0000,0000,,time to take a fresh look at this. So, Dialogue: 0,0:20:01.44,0:20:04.28,Default,,0000,0000,0000,,since class is the fundamental organizational\Nunit we have Dialogue: 0,0:20:04.28,0:20:07.12,Default,,0000,0000,0000,,to work with, it's time to work at what Dialogue: 0,0:20:07.12,0:20:10.82,Default,,0000,0000,0000,,constitutes a good class. Which principles\Nare gonna lead Dialogue: 0,0:20:10.82,0:20:14.29,Default,,0000,0000,0000,,us to be able to write small classes. Dialogue: 0,0:20:14.29,0:20:19.93,Default,,0000,0000,0000,,So, how do we write small classes? To make Dialogue: 0,0:20:19.93,0:20:23.54,Default,,0000,0000,0000,,small classes, I think, and this is not just Dialogue: 0,0:20:23.54,0:20:25.06,Default,,0000,0000,0000,,my opinion. It's a lot of peoples' opinion.\NThe Dialogue: 0,0:20:25.06,0:20:28.71,Default,,0000,0000,0000,,most important thing we should assure is that\Nour Dialogue: 0,0:20:28.71,0:20:35.08,Default,,0000,0000,0000,,class has one responsibility. And that it\Nhas small Dialogue: 0,0:20:35.08,0:20:38.13,Default,,0000,0000,0000,,methods. Dialogue: 0,0:20:38.13,0:20:41.18,Default,,0000,0000,0000,,All the properties of a class should be cohesive Dialogue: 0,0:20:41.18,0:20:44.74,Default,,0000,0000,0000,,to the abstraction that the class is modeling.\NIf Dialogue: 0,0:20:44.74,0:20:46.50,Default,,0000,0000,0000,,you have properties that you only use in one Dialogue: 0,0:20:46.50,0:20:50.37,Default,,0000,0000,0000,,or two methods, that's probably something\Nelse that shouldn't Dialogue: 0,0:20:50.37,0:20:53.43,Default,,0000,0000,0000,,be in there. Dialogue: 0,0:20:53.43,0:20:55.17,Default,,0000,0000,0000,,Finding a good name for a class will also Dialogue: 0,0:20:55.17,0:20:59.03,Default,,0000,0000,0000,,help us keep it focused on a single responsibility. Dialogue: 0,0:20:59.03,0:21:01.48,Default,,0000,0000,0000,,I sometimes talk to the class. Have you ever Dialogue: 0,0:21:01.48,0:21:03.87,Default,,0000,0000,0000,,heard the concept of talking to the rubber\Nduck? Dialogue: 0,0:21:03.87,0:21:05.77,Default,,0000,0000,0000,,Or just explaining your problem to someone?\NThey don't Dialogue: 0,0:21:05.77,0:21:08.22,Default,,0000,0000,0000,,even have to respond, and it helps you figure Dialogue: 0,0:21:08.22,0:21:09.18,Default,,0000,0000,0000,,it out. Dialogue: 0,0:21:09.18,0:21:11.06,Default,,0000,0000,0000,,Sometimes I just ask my class, hey class,\Nwhat Dialogue: 0,0:21:11.06,0:21:13.61,Default,,0000,0000,0000,,do you do? And if it comes out with Dialogue: 0,0:21:13.61,0:21:16.30,Default,,0000,0000,0000,,a long list, you've got a problem. Dialogue: 0,0:21:16.30,0:21:18.54,Default,,0000,0000,0000,,So, the main tools we're gonna use to create Dialogue: 0,0:21:18.54,0:21:23.52,Default,,0000,0000,0000,,new classes from existing code, not from scratch,\Nbut Dialogue: 0,0:21:23.52,0:21:26.72,Default,,0000,0000,0000,,from existing code, is the extract class and\Nmove Dialogue: 0,0:21:26.72,0:21:29.95,Default,,0000,0000,0000,,method refactorings, which we're gonna go\Nthrough here. Dialogue: 0,0:21:29.95,0:21:35.81,Default,,0000,0000,0000,,So, those characteristics of well-designed\Nclass. Single responsibility. Cohesive Dialogue: 0,0:21:35.81,0:21:38.86,Default,,0000,0000,0000,,around a set of properties. Additionally,\Nit has a Dialogue: 0,0:21:38.86,0:21:43.46,Default,,0000,0000,0000,,small public interface that, preferably, handles\Na handful of Dialogue: 0,0:21:43.46,0:21:46.55,Default,,0000,0000,0000,,methods at the most. That it implements a\Nsingle Dialogue: 0,0:21:46.55,0:21:49.77,Default,,0000,0000,0000,,use-case, if possible, and that the primary\Nlogic is Dialogue: 0,0:21:49.77,0:21:52.61,Default,,0000,0000,0000,,expressed in a composed method. Dialogue: 0,0:21:52.61,0:21:53.87,Default,,0000,0000,0000,,That last one, I'm not gonna be covering the Dialogue: 0,0:21:53.87,0:21:56.87,Default,,0000,0000,0000,,composed method. That's a whole nother talk.\NBut you Dialogue: 0,0:21:56.87,0:21:59.39,Default,,0000,0000,0000,,should check that practice out. It can really\Nclarify Dialogue: 0,0:21:59.39,0:22:02.65,Default,,0000,0000,0000,,code and make it much, much more understandable. Dialogue: 0,0:22:02.65,0:22:04.69,Default,,0000,0000,0000,,So, let's look at the code we should have Dialogue: 0,0:22:04.69,0:22:06.97,Default,,0000,0000,0000,,been driving towards as soon as the string\Noption Dialogue: 0,0:22:06.97,0:22:10.65,Default,,0000,0000,0000,,type showed up. We're gonna imagine right\Nnow that Dialogue: 0,0:22:10.65,0:22:12.87,Default,,0000,0000,0000,,we have a string sheet, and we can write Dialogue: 0,0:22:12.87,0:22:16.32,Default,,0000,0000,0000,,CommandLineOptions the way we would have with\Nthe knowledge Dialogue: 0,0:22:16.32,0:22:19.25,Default,,0000,0000,0000,,that we have now. Dialogue: 0,0:22:19.25,0:22:23.23,Default,,0000,0000,0000,,That needs to support boolean, string, and\Ninteger options. Dialogue: 0,0:22:23.23,0:22:26.85,Default,,0000,0000,0000,,And remember, we have our tests at our back, Dialogue: 0,0:22:26.85,0:22:30.22,Default,,0000,0000,0000,,making sure that we don't break anything. Dialogue: 0,0:22:30.22,0:22:33.72,Default,,0000,0000,0000,,And, here was my first take at it on Dialogue: 0,0:22:33.72,0:22:38.09,Default,,0000,0000,0000,,what I'd write. The class is twenty-eight\Nlines long. Dialogue: 0,0:22:38.09,0:22:40.80,Default,,0000,0000,0000,,It is cohesive around the properties. When\Nwe're done, Dialogue: 0,0:22:40.80,0:22:43.19,Default,,0000,0000,0000,,most of the methods are gonna deal with the, Dialogue: 0,0:22:43.19,0:22:46.83,Default,,0000,0000,0000,,the hash of options and the array of args. Dialogue: 0,0:22:46.83,0:22:51.27,Default,,0000,0000,0000,,It has a single primary responsibility. Manage\Na collection Dialogue: 0,0:22:51.27,0:22:54.18,Default,,0000,0000,0000,,of option objects. Dialogue: 0,0:22:54.18,0:22:57.60,Default,,0000,0000,0000,,So now we've introduced a collaborator. It\Nalso manufactures Dialogue: 0,0:22:57.60,0:23:00.75,Default,,0000,0000,0000,,the option objects, which I could abstract\Nto another Dialogue: 0,0:23:00.75,0:23:02.97,Default,,0000,0000,0000,,class. But for the moment, I'm gonna leave\Nit. Dialogue: 0,0:23:02.97,0:23:05.57,Default,,0000,0000,0000,,If I find it hurts in the future, then Dialogue: 0,0:23:05.57,0:23:09.33,Default,,0000,0000,0000,,I'll change it. That's my general rule. My\Nguideline. Dialogue: 0,0:23:09.33,0:23:12.79,Default,,0000,0000,0000,,Is I refactor when it hurts. When making a Dialogue: 0,0:23:12.79,0:23:15.52,Default,,0000,0000,0000,,change hurts, that's the time to refactor. Dialogue: 0,0:23:15.52,0:23:20.25,Default,,0000,0000,0000,,My CommandLineOptions class has a small public\Ninterface. Just Dialogue: 0,0:23:20.25,0:23:23.22,Default,,0000,0000,0000,,two methods, valid? and value. And it has\Nno Dialogue: 0,0:23:23.22,0:23:27.00,Default,,0000,0000,0000,,hard-coded external dependencies yet. I could\Nmess that up Dialogue: 0,0:23:27.00,0:23:29.96,Default,,0000,0000,0000,,and introduce those, but we're gonna avoid\Nthat. Dialogue: 0,0:23:29.96,0:23:32.49,Default,,0000,0000,0000,,Another interesting characteristic is that,\Nis that there are Dialogue: 0,0:23:32.49,0:23:36.45,Default,,0000,0000,0000,,no conditional statements in this class, and\Nwe're gonna Dialogue: 0,0:23:36.45,0:23:41.20,Default,,0000,0000,0000,,keep it that way. In Sandi Metz's 2009 Gerupo?? Dialogue: 0,0:23:41.20,0:23:44.76,Default,,0000,0000,0000,,talk, on the Solid Principles, she said something\Nalong Dialogue: 0,0:23:44.76,0:23:48.17,Default,,0000,0000,0000,,the lines of, a conditional in an OO language Dialogue: 0,0:23:48.17,0:23:49.92,Default,,0000,0000,0000,,is a smell. Dialogue: 0,0:23:49.92,0:23:53.82,Default,,0000,0000,0000,,And that's a really powerful statement. I\Ndon't think Dialogue: 0,0:23:53.82,0:23:56.55,Default,,0000,0000,0000,,Sandi's saying that we can't use conditionals\Nin our Dialogue: 0,0:23:56.55,0:24:01.88,Default,,0000,0000,0000,,code, but that we use conditionals to hide\Nabstractions. Dialogue: 0,0:24:01.88,0:24:05.23,Default,,0000,0000,0000,,To hide our ducks. Dialogue: 0,0:24:05.23,0:24:06.61,Default,,0000,0000,0000,,The first time I saw that talk, I don't Dialogue: 0,0:24:06.61,0:24:08.02,Default,,0000,0000,0000,,even know if I heard her say it. It Dialogue: 0,0:24:08.02,0:24:11.05,Default,,0000,0000,0000,,was when I went back and rewatched it. I Dialogue: 0,0:24:11.05,0:24:14.21,Default,,0000,0000,0000,,thought, really? Then, as the years have gone\Non Dialogue: 0,0:24:14.21,0:24:16.40,Default,,0000,0000,0000,,and I've been working, I've gotten to the\Npoint Dialogue: 0,0:24:16.40,0:24:18.54,Default,,0000,0000,0000,,where I agree with her. Dialogue: 0,0:24:18.54,0:24:20.96,Default,,0000,0000,0000,,If you have a lot of conditionals in a Dialogue: 0,0:24:20.96,0:24:24.42,Default,,0000,0000,0000,,class, you have probably missed a concept\Nthat should Dialogue: 0,0:24:24.42,0:24:28.85,Default,,0000,0000,0000,,be abstracted out of it. Dialogue: 0,0:24:28.85,0:24:31.71,Default,,0000,0000,0000,,So the initialize and option method from our\Nprevious Dialogue: 0,0:24:31.71,0:24:35.23,Default,,0000,0000,0000,,implementation carry over unchanged. Except\Nthat we're gonna store Dialogue: 0,0:24:35.23,0:24:37.27,Default,,0000,0000,0000,,the options in a hash instead of just the Dialogue: 0,0:24:37.27,0:24:39.69,Default,,0000,0000,0000,,type. Dialogue: 0,0:24:39.69,0:24:42.08,Default,,0000,0000,0000,,My valid? method now simply asks all the options Dialogue: 0,0:24:42.08,0:24:45.25,Default,,0000,0000,0000,,if they're valid, and the value method simply\Nlooks Dialogue: 0,0:24:45.25,0:24:48.25,Default,,0000,0000,0000,,up the option hash and asks it for its Dialogue: 0,0:24:48.25,0:24:51.58,Default,,0000,0000,0000,,value. So, now we need to build the options. Dialogue: 0,0:24:51.58,0:24:53.83,Default,,0000,0000,0000,,We have to implement this. And this is where Dialogue: 0,0:24:53.83,0:24:56.76,Default,,0000,0000,0000,,we're gonna instantiate the objects that represent\Nthe boolean, Dialogue: 0,0:24:56.76,0:24:59.30,Default,,0000,0000,0000,,string, and integer options. Dialogue: 0,0:24:59.30,0:25:04.34,Default,,0000,0000,0000,,So, now we have the CommandLineOption class,\Nwe need Dialogue: 0,0:25:04.34,0:25:07.16,Default,,0000,0000,0000,,collaborators. In order to get anything done,\NCommandLineOption needs Dialogue: 0,0:25:07.16,0:25:10.97,Default,,0000,0000,0000,,option classes to manage. It's gonna have\Nthose objects. Dialogue: 0,0:25:10.97,0:25:13.67,Default,,0000,0000,0000,,So this is creating a dependency. And if we're Dialogue: 0,0:25:13.67,0:25:15.76,Default,,0000,0000,0000,,gonna create a dependency in our code, we\Ncan Dialogue: 0,0:25:15.76,0:25:18.02,Default,,0000,0000,0000,,do it in a way that's amenable to change, Dialogue: 0,0:25:18.02,0:25:18.86,Default,,0000,0000,0000,,or we can do it in a way that's Dialogue: 0,0:25:18.86,0:25:25.50,Default,,0000,0000,0000,,gonna make it hurt in the future. Dialogue: 0,0:25:25.50,0:25:28.02,Default,,0000,0000,0000,,You don't want to depend, or, excuse me, you Dialogue: 0,0:25:28.02,0:25:33.02,Default,,0000,0000,0000,,want to depend on abstractions, ot concretions.\NDepend on Dialogue: 0,0:25:33.02,0:25:36.50,Default,,0000,0000,0000,,the duck type, not the concrete type. In our Dialogue: 0,0:25:36.50,0:25:39.38,Default,,0000,0000,0000,,case, depend on the concept, the concept of\Nan Dialogue: 0,0:25:39.38,0:25:43.77,Default,,0000,0000,0000,,option. Not on the concrete types that implement\Nthat Dialogue: 0,0:25:43.77,0:25:46.63,Default,,0000,0000,0000,,abstraction. Dialogue: 0,0:25:46.63,0:25:49.90,Default,,0000,0000,0000,,In our case, option is the duck type. This Dialogue: 0,0:25:49.90,0:25:52.34,Default,,0000,0000,0000,,is the abstraction that I missed earlier,\Nwhen I Dialogue: 0,0:25:52.34,0:25:57.31,Default,,0000,0000,0000,,just kept going down the conditional logic\Npath. Dialogue: 0,0:25:57.31,0:26:00.42,Default,,0000,0000,0000,,It's really simple. It has a valid? method\Nand Dialogue: 0,0:26:00.42,0:26:07.38,Default,,0000,0000,0000,,a value method. String option, integer option,\Nand boolean Dialogue: 0,0:26:07.38,0:26:11.45,Default,,0000,0000,0000,,option, those are the concrete implementation\Nof the option Dialogue: 0,0:26:11.45,0:26:15.08,Default,,0000,0000,0000,,abstraction. All they need is a valid? and\Na Dialogue: 0,0:26:15.08,0:26:18.89,Default,,0000,0000,0000,,value method, and a consistent method of construction,\Nand Dialogue: 0,0:26:18.89,0:26:24.72,Default,,0000,0000,0000,,I can depend on the abstraction, not on the Dialogue: 0,0:26:24.72,0:26:26.26,Default,,0000,0000,0000,,concretions. Dialogue: 0,0:26:26.26,0:26:32.28,Default,,0000,0000,0000,,So, how do I do that? I could go Dialogue: 0,0:26:32.28,0:26:34.83,Default,,0000,0000,0000,,down the case statement road again and check\Nthe Dialogue: 0,0:26:34.83,0:26:37.98,Default,,0000,0000,0000,,option type, instantiating the correct type\Nof the option Dialogue: 0,0:26:37.98,0:26:40.97,Default,,0000,0000,0000,,based upon the symbol. But I'm not gonna do Dialogue: 0,0:26:40.97,0:26:44.20,Default,,0000,0000,0000,,that, cause that would tie CommandLineClass\Nto those concrete Dialogue: 0,0:26:44.20,0:26:47.00,Default,,0000,0000,0000,,types, which is what we're trying to avoid. Dialogue: 0,0:26:47.00,0:26:50.25,Default,,0000,0000,0000,,That creates a hard dependency between CommandLineOptions\Nclass and Dialogue: 0,0:26:50.25,0:26:54.88,Default,,0000,0000,0000,,those various classes. Instead, I'm gonna\Nuse the dynamic Dialogue: 0,0:26:54.88,0:26:58.31,Default,,0000,0000,0000,,capabilities of Ruby to instantiate those\Nobjects for us Dialogue: 0,0:26:58.31,0:27:01.13,Default,,0000,0000,0000,,using naming conventions. For string, we're\Ngoing to have Dialogue: 0,0:27:01.13,0:27:06.29,Default,,0000,0000,0000,,a string option. For booleans, boolean option.\NEt cetera. Dialogue: 0,0:27:06.29,0:27:09.11,Default,,0000,0000,0000,,I could do this even in many static languages. Dialogue: 0,0:27:09.11,0:27:13.19,Default,,0000,0000,0000,,So this isn't something that's specific to\NRuby. And Dialogue: 0,0:27:13.19,0:27:15.33,Default,,0000,0000,0000,,this is a very. This very simple change takes Dialogue: 0,0:27:15.33,0:27:19.33,Default,,0000,0000,0000,,out CommandLineOption class from depending\Non those concrete implementations Dialogue: 0,0:27:19.33,0:27:23.49,Default,,0000,0000,0000,,and flips it to depending on the abstraction. Dialogue: 0,0:27:23.49,0:27:27.51,Default,,0000,0000,0000,,This is dependency inversion from the Solid\NPrinciples, in Dialogue: 0,0:27:27.51,0:27:33.43,Default,,0000,0000,0000,,practice. Alternately, some other people have\Nsuggested, you could Dialogue: 0,0:27:33.43,0:27:36.19,Default,,0000,0000,0000,,use a hash and map from the string, boolean, Dialogue: 0,0:27:36.19,0:27:39.23,Default,,0000,0000,0000,,and integer symbols to the concrete classes,\Nkind of Dialogue: 0,0:27:39.23,0:27:41.46,Default,,0000,0000,0000,,like what Sandi did in her Gilded Rose Coda?? Dialogue: 0,0:27:41.46,0:27:44.32,Default,,0000,0000,0000,,solution earlier. Dialogue: 0,0:27:44.32,0:27:47.02,Default,,0000,0000,0000,,That's OK. But, it is an additional thing\Nthat Dialogue: 0,0:27:47.02,0:27:50.91,Default,,0000,0000,0000,,I have to maintain over time. It's a reason Dialogue: 0,0:27:50.91,0:27:54.90,Default,,0000,0000,0000,,to open the CommandLineOptions and change\Nit if I Dialogue: 0,0:27:54.90,0:27:58.42,Default,,0000,0000,0000,,have to add a new type of option. If Dialogue: 0,0:27:58.42,0:28:00.46,Default,,0000,0000,0000,,using the dynamic ability of Ruby bothers\Nyou, then Dialogue: 0,0:28:00.46,0:28:03.42,Default,,0000,0000,0000,,make a hash. Personally, I'm fine with using\Nthe Dialogue: 0,0:28:03.42,0:28:06.10,Default,,0000,0000,0000,,dynamic capabilities of my language. Dialogue: 0,0:28:06.10,0:28:10.78,Default,,0000,0000,0000,,So, in my case, I've inoculated CommandLineOptions\Nclass from Dialogue: 0,0:28:10.78,0:28:14.34,Default,,0000,0000,0000,,needing to change to support new option types.\NAnd Dialogue: 0,0:28:14.34,0:28:16.54,Default,,0000,0000,0000,,at this point, this class should be closed\Nfor Dialogue: 0,0:28:16.54,0:28:20.98,Default,,0000,0000,0000,,modification, but open for extension. Dialogue: 0,0:28:20.98,0:28:23.36,Default,,0000,0000,0000,,So, now we need to move the logic for Dialogue: 0,0:28:23.36,0:28:27.34,Default,,0000,0000,0000,,the various option types to the appropriate\Noption classes. Dialogue: 0,0:28:27.34,0:28:29.40,Default,,0000,0000,0000,,I decided to make a base class of option Dialogue: 0,0:28:29.40,0:28:32.03,Default,,0000,0000,0000,,for my concrete types to inherit from, because\Nthe Dialogue: 0,0:28:32.03,0:28:34.14,Default,,0000,0000,0000,,manner of initialization needs to be the same\Nfor Dialogue: 0,0:28:34.14,0:28:38.00,Default,,0000,0000,0000,,all of them. No sense of repeating that code. Dialogue: 0,0:28:38.00,0:28:40.73,Default,,0000,0000,0000,,And the subtypes have a cohesion around the\Nflag Dialogue: 0,0:28:40.73,0:28:43.09,Default,,0000,0000,0000,,attribute, and the wrong, excuse me, the flag\Nand Dialogue: 0,0:28:43.09,0:28:47.59,Default,,0000,0000,0000,,the raw value properties that in the code. Dialogue: 0,0:28:47.59,0:28:50.03,Default,,0000,0000,0000,,Here's the boolean option. This one I just\Nwrote Dialogue: 0,0:28:50.03,0:28:53.04,Default,,0000,0000,0000,,because the requirements are so simple. Booleans\Nare always Dialogue: 0,0:28:53.04,0:28:55.46,Default,,0000,0000,0000,,valid, and they just return the raw_value\Nfrom the Dialogue: 0,0:28:55.46,0:28:57.29,Default,,0000,0000,0000,,command line. If it's present, it's truthy.\NIf it's Dialogue: 0,0:28:57.29,0:29:01.56,Default,,0000,0000,0000,,nil it's falsey. Very simple. Dialogue: 0,0:29:01.56,0:29:03.19,Default,,0000,0000,0000,,But now we need to implement string option\Nand Dialogue: 0,0:29:03.19,0:29:06.20,Default,,0000,0000,0000,,integer option. And the logic for their validation\Nand Dialogue: 0,0:29:06.20,0:29:11.39,Default,,0000,0000,0000,,value extraction is in the old CommandLineOptions\Nclass. So, Dialogue: 0,0:29:11.39,0:29:13.87,Default,,0000,0000,0000,,on the left are the original CommandLineOptions'\Nvalid? and Dialogue: 0,0:29:13.87,0:29:17.23,Default,,0000,0000,0000,,value methods. On the right are those new\Nstring Dialogue: 0,0:29:17.23,0:29:19.12,Default,,0000,0000,0000,,option and integer option classes. Dialogue: 0,0:29:19.12,0:29:22.25,Default,,0000,0000,0000,,As you can see, the process of creating the Dialogue: 0,0:29:22.25,0:29:25.83,Default,,0000,0000,0000,,option class was simply picking apart and\Ndisassembling the Dialogue: 0,0:29:25.83,0:29:29.59,Default,,0000,0000,0000,,old command line option class. Moving the\Nlogic to Dialogue: 0,0:29:29.59,0:29:33.79,Default,,0000,0000,0000,,where it belongs, using a combination of extract\Nclass Dialogue: 0,0:29:33.79,0:29:37.82,Default,,0000,0000,0000,,and move method refactorings, we've really\Ncleaned up the Dialogue: 0,0:29:37.82,0:29:39.52,Default,,0000,0000,0000,,command option, CommandLineOptions. Dialogue: 0,0:29:39.52,0:29:45.19,Default,,0000,0000,0000,,Frankly, there's not much code left there\Nanymore. So, Dialogue: 0,0:29:45.19,0:29:48.10,Default,,0000,0000,0000,,now we can replace that nasty, hard to understand Dialogue: 0,0:29:48.10,0:29:53.34,Default,,0000,0000,0000,,valid? method with this. And the large value\Nmethod Dialogue: 0,0:29:53.34,0:29:57.51,Default,,0000,0000,0000,,with this. Dialogue: 0,0:29:57.51,0:30:03.13,Default,,0000,0000,0000,,To create the specs for the various option\Nclasses, Dialogue: 0,0:30:03.13,0:30:05.94,Default,,0000,0000,0000,,I moved the corresponding section from the\NCommandLineOptions spec Dialogue: 0,0:30:05.94,0:30:08.46,Default,,0000,0000,0000,,to the corresponding area for the particular\Ntype of Dialogue: 0,0:30:08.46,0:30:12.04,Default,,0000,0000,0000,,option, and then lightly reworked them and\Nthen I Dialogue: 0,0:30:12.04,0:30:14.12,Default,,0000,0000,0000,,worked them from red to green, as I went Dialogue: 0,0:30:14.12,0:30:17.44,Default,,0000,0000,0000,,through the process of extracting those classes\Nand moving Dialogue: 0,0:30:17.44,0:30:22.97,Default,,0000,0000,0000,,the code to those methods. Dialogue: 0,0:30:22.97,0:30:25.54,Default,,0000,0000,0000,,We've isolated abstractions here. And how\Ndo we do Dialogue: 0,0:30:25.54,0:30:30.20,Default,,0000,0000,0000,,that? We separate the what from the how, like Dialogue: 0,0:30:30.20,0:30:35.09,Default,,0000,0000,0000,,we've done in CommandLineOptions. We want\Nto move from Dialogue: 0,0:30:35.09,0:30:39.13,Default,,0000,0000,0000,,code that looks like this to code that looks Dialogue: 0,0:30:39.13,0:30:43.61,Default,,0000,0000,0000,,like this. Dialogue: 0,0:30:43.61,0:30:48.19,Default,,0000,0000,0000,,The original CommandLineOptions' valid? method\Ncontained all of the Dialogue: 0,0:30:48.19,0:30:51.60,Default,,0000,0000,0000,,how. The refactored valid? method says what\Nwe want Dialogue: 0,0:30:51.60,0:30:55.23,Default,,0000,0000,0000,,done for us. That's it. All of the how Dialogue: 0,0:30:55.23,0:30:58.03,Default,,0000,0000,0000,,has moved to the collaborators of our main\Nclass, Dialogue: 0,0:30:58.03,0:31:02.33,Default,,0000,0000,0000,,in this case, StringOption, boolean option,\Nand IntegerOption. Dialogue: 0,0:31:02.33,0:31:06.22,Default,,0000,0000,0000,,We want to move from that looks like this Dialogue: 0,0:31:06.22,0:31:08.86,Default,,0000,0000,0000,,to code that looks like this. Move the nitty Dialogue: 0,0:31:08.86,0:31:12.22,Default,,0000,0000,0000,,gritty details of your code out to the leaves Dialogue: 0,0:31:12.22,0:31:16.47,Default,,0000,0000,0000,,of your system. And let the center be a Dialogue: 0,0:31:16.47,0:31:17.39,Default,,0000,0000,0000,,coordinator. Dialogue: 0,0:31:17.39,0:31:21.32,Default,,0000,0000,0000,,So, when we're done with this, this is what Dialogue: 0,0:31:21.32,0:31:25.58,Default,,0000,0000,0000,,our CommandLineOptions class looks like. These\Nare our public Dialogue: 0,0:31:25.58,0:31:30.01,Default,,0000,0000,0000,,methods. It provides a very small surface.\NAnd it Dialogue: 0,0:31:30.01,0:31:32.73,Default,,0000,0000,0000,,fulfills the use case. And these are the private Dialogue: 0,0:31:32.73,0:31:35.90,Default,,0000,0000,0000,,implementation cruft. It's necessary, but\Nno one really needs Dialogue: 0,0:31:35.90,0:31:37.48,Default,,0000,0000,0000,,to go poking around in here, and I've made Dialogue: 0,0:31:37.48,0:31:41.36,Default,,0000,0000,0000,,it obvious by declaring these methods private. Dialogue: 0,0:31:41.36,0:31:44.35,Default,,0000,0000,0000,,They're for me. Not for you. Dialogue: 0,0:31:44.35,0:31:46.74,Default,,0000,0000,0000,,So in the end, the sum total of the Dialogue: 0,0:31:46.74,0:31:50.36,Default,,0000,0000,0000,,implementation of the public interface, and\Nit's all delegated. Dialogue: 0,0:31:50.36,0:31:54.33,Default,,0000,0000,0000,,All delegated. Dialogue: 0,0:31:54.33,0:31:56.04,Default,,0000,0000,0000,,So in the process of making the specs pass, Dialogue: 0,0:31:56.04,0:31:58.85,Default,,0000,0000,0000,,I commented out that dreamed up code as I Dialogue: 0,0:31:58.85,0:32:01.28,Default,,0000,0000,0000,,went through the process, and then one by\None Dialogue: 0,0:32:01.28,0:32:04.15,Default,,0000,0000,0000,,I wrote the examples and uncommented the code\Nand Dialogue: 0,0:32:04.15,0:32:09.25,Default,,0000,0000,0000,,made them pass, working from red to green. Dialogue: 0,0:32:09.25,0:32:16.25,Default,,0000,0000,0000,,Then, because nothing is ever really done,\Nmy buddy Dialogue: 0,0:32:17.00,0:32:20.88,Default,,0000,0000,0000,,says, hey. Any chance you could add the ability Dialogue: 0,0:32:20.88,0:32:22.74,Default,,0000,0000,0000,,for me to pass an array of values for Dialogue: 0,0:32:22.74,0:32:24.16,Default,,0000,0000,0000,,an option? Dialogue: 0,0:32:24.16,0:32:28.32,Default,,0000,0000,0000,,So, to implement this new requirement, I only\Nneed Dialogue: 0,0:32:28.32,0:32:31.64,Default,,0000,0000,0000,,the new array option class. So I write a Dialogue: 0,0:32:31.64,0:32:37.78,Default,,0000,0000,0000,,spec example. Make it fail. Then create the\NArrayOption Dialogue: 0,0:32:37.78,0:32:43.26,Default,,0000,0000,0000,,class, and I'm done. And this particular example,\Nmy Dialogue: 0,0:32:43.26,0:32:48.60,Default,,0000,0000,0000,,OptionClass is inheriting from the OptionWithContent\Nsuperclass. And, cause Dialogue: 0,0:32:48.60,0:32:50.65,Default,,0000,0000,0000,,I actually went through this and realized\Nthat strings, Dialogue: 0,0:32:50.65,0:32:54.20,Default,,0000,0000,0000,,integers, and arrays all have content, so\NI abstracted Dialogue: 0,0:32:54.20,0:32:56.39,Default,,0000,0000,0000,,that superclass and, in this case, all I have Dialogue: 0,0:32:56.39,0:32:58.96,Default,,0000,0000,0000,,to do is write the value method of that Dialogue: 0,0:32:58.96,0:33:03.33,Default,,0000,0000,0000,,particular type and I'm done. Dialogue: 0,0:33:03.33,0:33:07.20,Default,,0000,0000,0000,,And it works. Dialogue: 0,0:33:07.20,0:33:12.82,Default,,0000,0000,0000,,So, we now have a CommandLineOption class\Nthat's closed Dialogue: 0,0:33:12.82,0:33:17.52,Default,,0000,0000,0000,,for modification, but open for extension.\NI could all Dialogue: 0,0:33:17.52,0:33:22.35,Default,,0000,0000,0000,,float types, decimal types, other types of\Noptions, and Dialogue: 0,0:33:22.35,0:33:24.23,Default,,0000,0000,0000,,I don't have to go back and touch that Dialogue: 0,0:33:24.23,0:33:25.92,Default,,0000,0000,0000,,class again. Dialogue: 0,0:33:25.92,0:33:29.39,Default,,0000,0000,0000,,We have small, easy to understand option classes\Nthat Dialogue: 0,0:33:29.39,0:33:36.39,Default,,0000,0000,0000,,have a single responsibility. Oops, excuse\Nme. Dialogue: 0,0:33:43.56,0:33:46.91,Default,,0000,0000,0000,,We can. So, we have a easy to understand Dialogue: 0,0:33:46.91,0:33:50.12,Default,,0000,0000,0000,,option classes that have a single responsibility,\Nand easy Dialogue: 0,0:33:50.12,0:33:54.10,Default,,0000,0000,0000,,to compose together with that CommandLineOption\Nclass. And we Dialogue: 0,0:33:54.10,0:33:56.35,Default,,0000,0000,0000,,can simply create new option types and have\Nthem Dialogue: 0,0:33:56.35,0:34:00.89,Default,,0000,0000,0000,,instantiated by convention. Dialogue: 0,0:34:00.89,0:34:04.21,Default,,0000,0000,0000,,My name is Mark Menard. My company's Enable\NLabs. Dialogue: 0,0:34:04.21,0:34:07.03,Default,,0000,0000,0000,,We do full lifecycle business productivity\Nand sass app Dialogue: 0,0:34:07.03,0:34:10.33,Default,,0000,0000,0000,,development, from napkin to production, as\NI say. And, Dialogue: 0,0:34:10.33,0:34:12.08,Default,,0000,0000,0000,,I'm gonna be around the conference, so let's\Nget Dialogue: 0,0:34:12.08,0:34:13.44,Default,,0000,0000,0000,,together and talk about some code. Dialogue: 0,0:34:13.44,0:34:14.67,Default,,0000,0000,0000,,And we can do some questions.