1 00:00:17,300 --> 00:00:19,410 MARK MENARD: So, many thanks to the organizers 2 00:00:19,410 --> 00:00:21,349 here at RailsConf. This is my first time 3 00:00:21,349 --> 00:00:23,890 talking at RailsConf. It's, frankly, 4 00:00:23,890 --> 00:00:25,250 kind of intimidating to be up here 5 00:00:25,250 --> 00:00:27,750 and see so many people out there. 6 00:00:27,750 --> 00:00:29,550 My name is Mark Menard. I'm gonna be talking 7 00:00:29,550 --> 00:00:32,619 about small code today. And I've got a lot 8 00:00:32,619 --> 00:00:35,159 of code. About seventy-nine slides. A hundred and thirty-seven 9 00:00:35,159 --> 00:00:37,940 transitions. Not quite as much as Sandi had, but 10 00:00:37,940 --> 00:00:40,480 it's a lot to get through. So let's get 11 00:00:40,480 --> 00:00:41,680 going. 12 00:00:41,680 --> 00:00:44,250 So, I'm just gonna let this quote up there 13 00:00:44,250 --> 00:00:44,809 sink in. 14 00:00:44,809 --> 00:00:51,809 So. All of us have that file filled with 15 00:00:53,250 --> 00:00:56,609 code that you just don't want to open. As 16 00:00:56,609 --> 00:01:00,559 you heard earlier, maybe it's your User class. That 17 00:01:00,559 --> 00:01:02,539 class that has comments like, woe to ye who 18 00:01:02,539 --> 00:01:05,880 edit here. The problem with this code is that 19 00:01:05,880 --> 00:01:10,860 it does live forever. It encapsulates business logic that 20 00:01:10,860 --> 00:01:13,039 ends up getting duplicated elsewhere, cause no one wants 21 00:01:13,039 --> 00:01:15,450 to go in there and look at that code. 22 00:01:15,450 --> 00:01:17,520 It's also very hard to understand. 23 00:01:17,520 --> 00:01:19,970 I'm gonna be talking about ways to avoid this 24 00:01:19,970 --> 00:01:24,580 situation. I'm gonna be talking about code. Code at 25 00:01:24,580 --> 00:01:28,000 the class level and the method level. Having small 26 00:01:28,000 --> 00:01:31,020 code at the class and method level is fundamental 27 00:01:31,020 --> 00:01:34,069 to being able to create systems that are composed, 28 00:01:34,069 --> 00:01:37,599 composed of small, understandable parts. 29 00:01:37,599 --> 00:01:39,110 I'm gonna lay out a few base concepts so 30 00:01:39,110 --> 00:01:41,739 that we can start with a clean sheet and 31 00:01:41,739 --> 00:01:44,340 on the same page. I think there's a lot 32 00:01:44,340 --> 00:01:46,420 of problems with what people conceive of as small 33 00:01:46,420 --> 00:01:49,179 or well-designed code. 34 00:01:49,179 --> 00:01:50,929 It's not about the actual amount of code you 35 00:01:50,929 --> 00:01:54,640 write, but how the code is organized and the 36 00:01:54,640 --> 00:01:59,220 size of the units of code. Fundamentally, writing small 37 00:01:59,220 --> 00:02:01,890 code is really design discipline, because the only way 38 00:02:01,890 --> 00:02:04,729 you can write small code is use good design 39 00:02:04,729 --> 00:02:07,569 and refactoring. 40 00:02:07,569 --> 00:02:09,840 Design and refactoring the way we write small code. 41 00:02:09,840 --> 00:02:12,250 You can't just sit down and write small code, 42 00:02:12,250 --> 00:02:14,750 perfectly well-designed code on the first draft. It doesn't 43 00:02:14,750 --> 00:02:18,450 work that way. It's iterative process. 44 00:02:18,450 --> 00:02:23,260 So what do I mean by small? It's not 45 00:02:23,260 --> 00:02:27,610 about total line count. Well-designed code will typically have 46 00:02:27,610 --> 00:02:30,750 more lines of code than bad code. Just the 47 00:02:30,750 --> 00:02:33,700 overhead of declaring methods and classes is gonna increase 48 00:02:33,700 --> 00:02:36,230 your line count. 49 00:02:36,230 --> 00:02:39,480 It's not about method count. Well-factored code's gonna have 50 00:02:39,480 --> 00:02:46,480 more, smaller methods. It's not about class count. Well-designed 51 00:02:47,500 --> 00:02:51,240 code is almost definitely going to have more classes 52 00:02:51,240 --> 00:02:53,380 than what I call undesigned code. 53 00:02:53,380 --> 00:02:57,530 Although I've seen some cases of over-abstraction, I find 54 00:02:57,530 --> 00:03:01,390 that's pretty rare unless someone goes pattern crazy. So 55 00:03:01,390 --> 00:03:03,360 small code is definitely not about decreasing the number 56 00:03:03,360 --> 00:03:08,460 of classes in your system. It's about well-designed, it's 57 00:03:08,460 --> 00:03:12,340 about well-designed classes that aren't poorly designed. 58 00:03:12,340 --> 00:03:14,460 So what do I mean by small? Small methods, 59 00:03:14,460 --> 00:03:17,290 small classes. Small methods are the foundation of writing 60 00:03:17,290 --> 00:03:21,450 small code. Without the ability to decompose large methods 61 00:03:21,450 --> 00:03:25,180 into small methods, we cannot write small code. And 62 00:03:25,180 --> 00:03:28,180 without small methods, we can't raise the level of 63 00:03:28,180 --> 00:03:30,740 abstraction. 64 00:03:30,740 --> 00:03:32,709 To write small code, we have to be able 65 00:03:32,709 --> 00:03:36,350 to decompose large classes into smaller classes, and abstract 66 00:03:36,350 --> 00:03:40,400 responsibilities out of them and separate them on higher-level, 67 00:03:40,400 --> 00:03:44,180 and base them on higher-level abstractions. 68 00:03:44,180 --> 00:03:46,459 It's important that our classes are small, because small 69 00:03:46,459 --> 00:03:52,530 classes are what lead to reusability and composability. 70 00:03:52,530 --> 00:03:59,530 So, why should we strive for small code? Why 71 00:04:00,640 --> 00:04:05,020 is it important? 72 00:04:05,020 --> 00:04:08,880 We don't know what the future is going to 73 00:04:08,880 --> 00:04:15,680 bring. Your software requirements are going to change. Software 74 00:04:15,680 --> 00:04:19,339 must be amenable to change. Any system of software 75 00:04:19,339 --> 00:04:23,370 that's going to have a long, successful life, is 76 00:04:23,370 --> 00:04:28,250 going to change significantly. Small code is simply easier 77 00:04:28,250 --> 00:04:32,160 to work with than large, complex code. If the 78 00:04:32,160 --> 00:04:34,990 requirements of your software are never gonna change, you 79 00:04:34,990 --> 00:04:38,090 can ignore everything that I have to say here. 80 00:04:38,090 --> 00:04:45,090 But I doubt that that's the case. 81 00:04:45,409 --> 00:04:47,729 We should write small code because it helps us 82 00:04:47,729 --> 00:04:52,199 raise the level of abstraction in our code. It's 83 00:04:52,199 --> 00:04:54,029 one of the most important things we do to 84 00:04:54,029 --> 00:05:00,289 create readable, understandable code. All good design is really 85 00:05:00,289 --> 00:05:05,340 driving toward expressing ubiquitous language of our problem domain 86 00:05:05,340 --> 00:05:07,960 in our code. 87 00:05:07,960 --> 00:05:10,879 The combination of small methods and small classes is 88 00:05:10,879 --> 00:05:13,870 going to help us raise that level of abstraction 89 00:05:13,870 --> 00:05:20,870 and express those higher-level domain concepts. 90 00:05:21,229 --> 00:05:22,870 We should also write small code so we can 91 00:05:22,870 --> 00:05:27,960 effectively use composition. Small classes and small methods compose 92 00:05:27,960 --> 00:05:31,819 together well. As we compose instances of small objects 93 00:05:31,819 --> 00:05:36,930 together, our systems will become message-based. In order to 94 00:05:36,930 --> 00:05:39,909 build systems that are message-based, we have to use 95 00:05:39,909 --> 00:05:44,569 delegation. And small, composable parts. Small code makes small 96 00:05:44,569 --> 00:05:47,779 composable parts. It's gonna help our software have flexibility 97 00:05:47,779 --> 00:05:50,110 and lead to a suppleness over time, and allow 98 00:05:50,110 --> 00:05:52,900 us to follow those messages. And eventually we're gonna 99 00:05:52,900 --> 00:05:57,699 see, find our duck types. 100 00:05:57,699 --> 00:06:01,430 And all this is about enabling future change. And 101 00:06:01,430 --> 00:06:06,509 accommodate the future requirements without a forklift replacement. 102 00:06:06,509 --> 00:06:12,490 So the goal: small units of understandable code that 103 00:06:12,490 --> 00:06:18,099 are amenable to change. 104 00:06:18,099 --> 00:06:22,300 Our primary tools are extract method and extract class. 105 00:06:22,300 --> 00:06:25,349 Longer methods are harder to understand than short methods. 106 00:06:25,349 --> 00:06:26,719 And most of the time, we can shorten a 107 00:06:26,719 --> 00:06:30,869 method simply by using the abstract method refactoring. I 108 00:06:30,869 --> 00:06:35,069 use this thing all the time when I'm coding. 109 00:06:35,069 --> 00:06:36,240 And once we have a set of methods that 110 00:06:36,240 --> 00:06:39,680 are coherent around a concept, then we can look 111 00:06:39,680 --> 00:06:43,059 to abstract those into a separate class and move 112 00:06:43,059 --> 00:06:47,289 the methods to that new class. 113 00:06:47,289 --> 00:06:50,129 So, I'm gonna be using the example of a 114 00:06:50,129 --> 00:06:53,680 command line option parser that handles booleans to start 115 00:06:53,680 --> 00:06:55,650 with, and then we're gonna see where the future 116 00:06:55,650 --> 00:06:57,020 takes us. 117 00:06:57,020 --> 00:07:01,770 So, with the command line, I want to be 118 00:07:01,770 --> 00:07:05,749 able to run some Ruby program dash v. And 119 00:07:05,749 --> 00:07:11,580 handle boolean options. That's where we're gonna start. 120 00:07:11,580 --> 00:07:13,550 In my Ruby program, I want to define what 121 00:07:13,550 --> 00:07:20,550 options I'm looking for, using this simple DSL. And 122 00:07:21,330 --> 00:07:22,669 then I want to be able to consume it 123 00:07:22,669 --> 00:07:26,839 like this. If options.has and then a particular option, 124 00:07:26,839 --> 00:07:28,180 I do something. 125 00:07:28,180 --> 00:07:34,499 Putting it all together. The DSL, the program at 126 00:07:34,499 --> 00:07:36,389 the top, the DSL, and then how we actually 127 00:07:36,389 --> 00:07:42,749 consume that options object. Pretty simple. 128 00:07:42,749 --> 00:07:47,939 Here's my spec. It's pretty simple. It's true if 129 00:07:47,939 --> 00:07:49,899 the option is defined and it's present on the 130 00:07:49,899 --> 00:07:54,300 command line. And it's false if it's not. 131 00:07:54,300 --> 00:07:56,469 So I run my specs and I get two 132 00:07:56,469 --> 00:08:03,469 failures. Yes, I used TDD. So, here's my implementation 133 00:08:04,719 --> 00:08:09,439 that fits on one slide. Pretty simply, I store 134 00:08:09,439 --> 00:08:12,550 the defined options in an array, and I store 135 00:08:12,550 --> 00:08:17,339 the arguments, the argv for later reference. Then I 136 00:08:17,339 --> 00:08:18,800 have a has method that checks to see if 137 00:08:18,800 --> 00:08:20,860 the option is defined. If it's present in the 138 00:08:20,860 --> 00:08:21,629 argv. 139 00:08:21,629 --> 00:08:25,270 And then I've got my option method, which implements 140 00:08:25,270 --> 00:08:30,770 my simple DSL. Nice and readable. Fits on one 141 00:08:30,770 --> 00:08:35,440 slide. Probably very comprehendable. 142 00:08:35,440 --> 00:08:40,360 So I run my tests. Zero failures. They pass. 143 00:08:40,360 --> 00:08:45,649 I'm done. I get to go home until the 144 00:08:45,649 --> 00:08:48,660 future comes along. And my workmate comes along and 145 00:08:48,660 --> 00:08:51,500 says, hey, I really like that library. But, could 146 00:08:51,500 --> 00:08:54,500 we handle string options? 147 00:08:54,500 --> 00:08:59,610 Sounds pretty simple. Pretty straightforward. So I think about 148 00:08:59,610 --> 00:09:01,110 that, and I come up with a small extension 149 00:09:01,110 --> 00:09:04,110 to the DSL, to just pass a second argument 150 00:09:04,110 --> 00:09:06,300 as an option with a symbol representation of the 151 00:09:06,300 --> 00:09:08,019 option type. String, in this case. 152 00:09:08,019 --> 00:09:10,190 I also default to being a boolean so I 153 00:09:10,190 --> 00:09:12,110 don't have to change the code that other people 154 00:09:12,110 --> 00:09:15,149 have done. 155 00:09:15,149 --> 00:09:18,639 So, a string option. It's a little different than 156 00:09:18,639 --> 00:09:23,310 a boolean. It actually requires content. So now I 157 00:09:23,310 --> 00:09:26,519 need the concept of validation. If the string option 158 00:09:26,519 --> 00:09:28,430 is missing the content, it's not valid. There's no 159 00:09:28,430 --> 00:09:30,589 string there. 160 00:09:30,589 --> 00:09:33,430 So, then I'm gonna normalize how I get the 161 00:09:33,430 --> 00:09:35,519 values out of both those string options and those 162 00:09:35,519 --> 00:09:38,300 boolean options. You know, that value. This is gonna 163 00:09:38,300 --> 00:09:40,880 change the API, but sometimes you actually need to 164 00:09:40,880 --> 00:09:43,750 break the API to enable the future. 165 00:09:43,750 --> 00:09:46,690 And I'm doing it pretty early. I've only got 166 00:09:46,690 --> 00:09:50,970 one guy in my office using the library at 167 00:09:50,970 --> 00:09:53,209 the moment. So, again, putting it all together. I 168 00:09:53,209 --> 00:09:55,329 can pass the options on the command line. I 169 00:09:55,329 --> 00:09:58,050 define the options with the DSL, and here's how 170 00:09:58,050 --> 00:10:00,360 I use my valid? and my value methods to 171 00:10:00,360 --> 00:10:02,680 find out, get, find out if it's valued and 172 00:10:02,680 --> 00:10:05,639 get my values out. 173 00:10:05,639 --> 00:10:09,959 So, now here's the class that implements it. Again, 174 00:10:09,959 --> 00:10:12,949 on one slide. Probably not as readable. Probably not 175 00:10:12,949 --> 00:10:16,399 as comprehensible. We're going down what I call kind 176 00:10:16,399 --> 00:10:20,240 of the undesigned path. It's not too big. Thirty-one 177 00:10:20,240 --> 00:10:22,089 lines. But it's got issues. 178 00:10:22,089 --> 00:10:24,389 It's got a method that's definitely large. One that's 179 00:10:24,389 --> 00:10:27,800 looking on the verge of being large. It's got, 180 00:10:27,800 --> 00:10:29,980 for only handling booleans and strings, it has quite 181 00:10:29,980 --> 00:10:34,300 a bit of, of conditional complexity in it already. 182 00:10:34,300 --> 00:10:36,110 And as we're soon gonna see, it's not very 183 00:10:36,110 --> 00:10:37,690 amenable to change. 184 00:10:37,690 --> 00:10:39,639 So we'll look at the pieces and how they 185 00:10:39,639 --> 00:10:40,850 work, just so yo understand it. 186 00:10:40,850 --> 00:10:42,839 That's my initialize method. It creates a hash to 187 00:10:42,839 --> 00:10:44,819 store the options. Because we have to store the 188 00:10:44,819 --> 00:10:47,430 type now, not just that we have an option. 189 00:10:47,430 --> 00:10:50,500 It's either boolean or string. And the rest of 190 00:10:50,500 --> 00:10:54,560 the initialization is the same as it was before. 191 00:10:54,560 --> 00:10:57,259 And the valid method, we gotta iterate over the 192 00:10:57,259 --> 00:10:59,949 options, looking to see which ones are strings. So 193 00:10:59,949 --> 00:11:03,420 we're doing checking on type here. And, and trying 194 00:11:03,420 --> 00:11:06,190 to see whether they're present and they actually have 195 00:11:06,190 --> 00:11:06,589 content. 196 00:11:06,589 --> 00:11:08,720 Currently, string options are the only ones that need 197 00:11:08,720 --> 00:11:12,610 to validate. Boolean options, there's nothing really to validate. 198 00:11:12,610 --> 00:11:14,680 Either it's there or it's not. No validation. But 199 00:11:14,680 --> 00:11:15,920 strings, we have to. 200 00:11:15,920 --> 00:11:18,209 And the value method, it does a lot of 201 00:11:18,209 --> 00:11:21,410 stuff. Let's just pretend for a moment this method 202 00:11:21,410 --> 00:11:23,100 is a black box. We're gonna come back to 203 00:11:23,100 --> 00:11:26,089 it later. Cause this is, by far, the worst 204 00:11:26,089 --> 00:11:29,279 code in this current example. 205 00:11:29,279 --> 00:11:34,300 But, everything is spec'd. And all my specs are 206 00:11:34,300 --> 00:11:36,399 green. 207 00:11:36,399 --> 00:11:40,120 So let's talk about methods. Cause we've got some 208 00:11:40,120 --> 00:11:42,420 big ones and we need to clean them up. 209 00:11:42,420 --> 00:11:47,629 I call it the first rule of method, of 210 00:11:47,629 --> 00:11:51,589 methods. Do one thing. Do it well. Do only 211 00:11:51,589 --> 00:11:52,480 one thing. 212 00:11:52,480 --> 00:11:55,440 Harkens back to that Unix philosophy of tools that 213 00:11:55,440 --> 00:11:59,290 you string together with standard in, standard out. But 214 00:11:59,290 --> 00:12:01,149 how do we determine if a method is actually 215 00:12:01,149 --> 00:12:04,610 only doing one thing? This is where your level 216 00:12:04,610 --> 00:12:08,000 of abstraction and the abstract, abstractions in your code 217 00:12:08,000 --> 00:12:10,579 come into play. And you need to develop a 218 00:12:10,579 --> 00:12:13,399 feel for this over time. That you want one 219 00:12:13,399 --> 00:12:16,129 level of abstraction per method. 220 00:12:16,129 --> 00:12:18,629 If all of our statements are the same level 221 00:12:18,629 --> 00:12:22,529 of abstraction, and they're coherent around a purpose, then 222 00:12:22,529 --> 00:12:25,810 I consider that to be doing one thing. Doesn't 223 00:12:25,810 --> 00:12:27,620 mean it has to be one line in a 224 00:12:27,620 --> 00:12:30,220 method. 225 00:12:30,220 --> 00:12:32,399 I can't tell you how many times I've looked 226 00:12:32,399 --> 00:12:36,009 at code and seen a comment on a method 227 00:12:36,009 --> 00:12:37,730 that was, like, an excellent description of what the 228 00:12:37,730 --> 00:12:39,839 method did, and if you just took those words, 229 00:12:39,839 --> 00:12:42,160 bound together, they'd make a fantastic method name. But 230 00:12:42,160 --> 00:12:45,310 yet the method is named something else that isn't 231 00:12:45,310 --> 00:12:50,980 that descriptive. So use descriptive names. It's really critical. 232 00:12:50,980 --> 00:12:53,149 And the fewer arguments, the better. My personal goal 233 00:12:53,149 --> 00:12:57,670 is zero arguments on methods. One is OK. Two 234 00:12:57,670 --> 00:12:59,790 or three. That's when I start to think I've 235 00:12:59,790 --> 00:13:02,589 probably missed an abstraction in my code and I 236 00:13:02,589 --> 00:13:06,699 should go back and look at it. 237 00:13:06,699 --> 00:13:09,819 Separate queries from commands. If you query something and 238 00:13:09,819 --> 00:13:11,519 it looks like a query method and it changes 239 00:13:11,519 --> 00:13:14,170 the state of your object, it's hard to reason 240 00:13:14,170 --> 00:13:16,600 about, and people who consume your library will be 241 00:13:16,600 --> 00:13:20,569 confused by that. So separate those. 242 00:13:20,569 --> 00:13:24,589 And, don't repeat yourself. I know Sandi talked about 243 00:13:24,589 --> 00:13:27,810 this earlier, and it does take some judgment to 244 00:13:27,810 --> 00:13:31,089 know when it is time to remove the repetition. 245 00:13:31,089 --> 00:13:32,949 But you don't want to leave repetition over the 246 00:13:32,949 --> 00:13:35,750 long term, because it will come back to bite 247 00:13:35,750 --> 00:13:37,379 you. 248 00:13:37,379 --> 00:13:42,009 So, let's look at our methods. We've got repetition 249 00:13:42,009 --> 00:13:44,069 here. Both valid? and value are digging through the 250 00:13:44,069 --> 00:13:46,660 argv array to find the options from the command 251 00:13:46,660 --> 00:13:49,639 line. This is the perfect candidate for an extract 252 00:13:49,639 --> 00:13:53,779 method, abstraction. Refactoring. 253 00:13:53,779 --> 00:13:56,910 We have magic constants scattered around, and those are 254 00:13:56,910 --> 00:14:02,120 a strong indication that we've missed something. An abstraction. 255 00:14:02,120 --> 00:14:06,139 We're violating some other rules. It's hard to say 256 00:14:06,139 --> 00:14:11,149 either of these methods is really doing one thing. 257 00:14:11,149 --> 00:14:13,829 The code is definitely not at the same level 258 00:14:13,829 --> 00:14:17,930 of abstraction. Values digging, valid? is digging into the 259 00:14:17,930 --> 00:14:20,759 argv array and value is figuring out different divergent 260 00:14:20,759 --> 00:14:23,670 types and how to return their values. So we're 261 00:14:23,670 --> 00:14:25,800 gonna eliminate some of the repetition with the extract 262 00:14:25,800 --> 00:14:28,629 method refactoring. 263 00:14:28,629 --> 00:14:33,160 The extract method refactoring entails moving a part of 264 00:14:33,160 --> 00:14:35,420 a method into a new method, with a descriptive 265 00:14:35,420 --> 00:14:38,449 name, that's the naming part. And then calling the 266 00:14:38,449 --> 00:14:39,529 new method. 267 00:14:39,529 --> 00:14:42,310 This, this refactoring helps us keep the level of 268 00:14:42,310 --> 00:14:47,819 abstraction consistent in the method we're abstracting from. Here 269 00:14:47,819 --> 00:14:49,459 we have one expression on a method that's a 270 00:14:49,459 --> 00:14:52,839 high level of abstraction, and two statements that are 271 00:14:52,839 --> 00:14:56,529 a low level of abstraction. So we move the 272 00:14:56,529 --> 00:14:58,509 less abstract code to a new method with a 273 00:14:58,509 --> 00:15:01,930 descriptive name, and then we call the new method. 274 00:15:01,930 --> 00:15:04,920 And this results in the old method, method having 275 00:15:04,920 --> 00:15:09,990 a consistent level of abstraction. So back to our 276 00:15:09,990 --> 00:15:14,160 CommandLineOptions class, both valid? and value are digging through 277 00:15:14,160 --> 00:15:16,689 the argv collection to find the option value. So 278 00:15:16,689 --> 00:15:18,740 we're gonna abstract that code and get the raw 279 00:15:18,740 --> 00:15:22,110 value out of argv. 280 00:15:22,110 --> 00:15:23,749 Then we call the method from where the original 281 00:15:23,749 --> 00:15:27,519 logic was abstracted. Pretty simple. But now the code 282 00:15:27,519 --> 00:15:30,089 left behind in valid? and value says what I 283 00:15:30,089 --> 00:15:34,439 want. Not how to do it. 284 00:15:34,439 --> 00:15:37,759 The how has been moved to the abstracted method, 285 00:15:37,759 --> 00:15:40,470 raising the level of abstraction just a little bit 286 00:15:40,470 --> 00:15:44,240 in valid? and value. 287 00:15:44,240 --> 00:15:47,279 I'm going to do two more abstractions. I've abstracted 288 00:15:47,279 --> 00:15:51,540 the string option value method and the abstract content 289 00:15:51,540 --> 00:15:55,389 method. The naming of the abstracted methods is very 290 00:15:55,389 --> 00:16:00,540 important. They say what they do. 291 00:16:00,540 --> 00:16:03,339 But overall, I'm not happy with this code. It 292 00:16:03,339 --> 00:16:06,329 is more explanatory, but it's fairly complex and hard 293 00:16:06,329 --> 00:16:09,809 to understand. It's also not as small as it 294 00:16:09,809 --> 00:16:12,629 could be. The methods are large because I missed 295 00:16:12,629 --> 00:16:17,499 an abstraction. And we're gonna go find that now. 296 00:16:17,499 --> 00:16:19,730 I'm referencing the option type symbol to see if 297 00:16:19,730 --> 00:16:24,519 it's a string, which, that's a big smell. Then 298 00:16:24,519 --> 00:16:26,290 there are the magic constants used to dig into 299 00:16:26,290 --> 00:16:28,959 the argv element to find the constant within that 300 00:16:28,959 --> 00:16:34,240 particular string, the substring. If I was confident that 301 00:16:34,240 --> 00:16:36,709 I'd have no future added requirements for this class, 302 00:16:36,709 --> 00:16:40,639 I might leave this alone. It works. It's tested. 303 00:16:40,639 --> 00:16:43,220 Until my buddy comes to me and says, hey, 304 00:16:43,220 --> 00:16:45,300 I really like that library, but could we handle 305 00:16:45,300 --> 00:16:48,149 integers now? 306 00:16:48,149 --> 00:16:50,959 I could keep driving down this undesigned path I've 307 00:16:50,959 --> 00:16:53,910 been following, and complicate the valid? and value methods 308 00:16:53,910 --> 00:16:56,420 by switching on the type of the option and 309 00:16:56,420 --> 00:16:59,790 digging into those argv elements to find the value. 310 00:16:59,790 --> 00:17:03,689 But, this is our chance to make a break. 311 00:17:03,689 --> 00:17:07,380 And make our code more amenable to change. 312 00:17:07,380 --> 00:17:11,329 But, to illustrate the point, I'm gonna show you 313 00:17:11,329 --> 00:17:14,939 that undesign method, to show you the OO design 314 00:17:14,939 --> 00:17:18,199 actually matters. So we're gonna look at this. 315 00:17:18,199 --> 00:17:20,400 This is the undesigned, non OO version of this 316 00:17:20,400 --> 00:17:23,869 code. Is it horrible? I'll leave that to you 317 00:17:23,869 --> 00:17:27,119 to decide. Is it small? In my opinion, definitely 318 00:17:27,119 --> 00:17:30,120 not. It is not small, by any measure. The 319 00:17:30,120 --> 00:17:32,090 class is growing due to changes in specification. The 320 00:17:32,090 --> 00:17:36,490 valid? and value methods are being changed in lock 321 00:17:36,490 --> 00:17:39,170 step. That's a sure sign we've missed an abstraction 322 00:17:39,170 --> 00:17:41,850 or a duck type. And those methods are getting 323 00:17:41,850 --> 00:17:45,900 big and complicated. And now they're doing even more 324 00:17:45,900 --> 00:17:47,340 things. 325 00:17:47,340 --> 00:17:52,380 And we're just doing booleans, strings, and integers. Not 326 00:17:52,380 --> 00:17:53,340 that much. 327 00:17:53,340 --> 00:18:00,270 The code has tests. They all pass. That's good. 328 00:18:00,270 --> 00:18:03,670 But it's not satisfying. We've got those large methods 329 00:18:03,670 --> 00:18:07,880 and complex conditional logic. It's time to refactor now. 330 00:18:07,880 --> 00:18:10,570 To make the change easy. 331 00:18:10,570 --> 00:18:12,340 And now we've got the tests that are back, 332 00:18:12,340 --> 00:18:14,680 so we can do it without fear. 333 00:18:14,680 --> 00:18:18,420 And, I want to call your attention to a 334 00:18:18,420 --> 00:18:21,740 pattern that clearly emerges when we go down the 335 00:18:21,740 --> 00:18:25,720 non OO path here. We see checking the option 336 00:18:25,720 --> 00:18:30,940 type and divergent behavior based on the type. Don't 337 00:18:30,940 --> 00:18:36,270 reinvent the type system. If you have ducks, let 338 00:18:36,270 --> 00:18:37,930 them quack. 339 00:18:37,930 --> 00:18:40,710 In this example, the option types of boolean, string, 340 00:18:40,710 --> 00:18:44,080 and integer, those are our ducks. And I'll bet 341 00:18:44,080 --> 00:18:47,830 there's ducks in your code yearning to be free. 342 00:18:47,830 --> 00:18:51,410 And just a further confirmation that we're dealing with 343 00:18:51,410 --> 00:18:55,300 an abstraction or a duck, we see the testing 344 00:18:55,300 --> 00:18:59,190 option type again in the value method. Hidden inside 345 00:18:59,190 --> 00:19:02,130 the valid? and value method, there's a case statement 346 00:19:02,130 --> 00:19:04,700 here. It just didn't evolve that way as I 347 00:19:04,700 --> 00:19:05,560 was writing the code. 348 00:19:05,560 --> 00:19:08,720 I'm gonna show you that. You're gonna see that 349 00:19:08,720 --> 00:19:12,840 it's really clear now. 350 00:19:12,840 --> 00:19:14,650 Now it should be really obvious what the duck 351 00:19:14,650 --> 00:19:17,280 type is. If you have case statements like this 352 00:19:17,280 --> 00:19:24,280 in your code, you've missed an abstraction. Here, again, 353 00:19:24,540 --> 00:19:26,830 we clearly see the duck type. 354 00:19:26,830 --> 00:19:30,070 Now, I would guess, if I was writing this, 355 00:19:30,070 --> 00:19:32,420 as soon as I had the string type, I 356 00:19:32,420 --> 00:19:35,360 would have gone down the OO path. I just 357 00:19:35,360 --> 00:19:38,690 wanted to illustrate to you what an undesigned, non 358 00:19:38,690 --> 00:19:41,700 OO mess you can get yourself into if you 359 00:19:41,700 --> 00:19:45,570 keep riding the horse until it's dead. 360 00:19:45,570 --> 00:19:47,290 My dad had a saying hanging on his wall 361 00:19:47,290 --> 00:19:49,930 in his office. When the horse is dead, get 362 00:19:49,930 --> 00:19:52,060 off. 363 00:19:52,060 --> 00:19:54,810 But sometimes we don't realize the horse is dead 364 00:19:54,810 --> 00:19:58,160 and we just keep trying to go. Now it's 365 00:19:58,160 --> 00:20:01,440 time to take a fresh look at this. So, 366 00:20:01,440 --> 00:20:04,280 since class is the fundamental organizational unit we have 367 00:20:04,280 --> 00:20:07,120 to work with, it's time to work at what 368 00:20:07,120 --> 00:20:10,820 constitutes a good class. Which principles are gonna lead 369 00:20:10,820 --> 00:20:14,290 us to be able to write small classes. 370 00:20:14,290 --> 00:20:19,930 So, how do we write small classes? To make 371 00:20:19,930 --> 00:20:23,540 small classes, I think, and this is not just 372 00:20:23,540 --> 00:20:25,060 my opinion. It's a lot of peoples' opinion. The 373 00:20:25,060 --> 00:20:28,710 most important thing we should assure is that our 374 00:20:28,710 --> 00:20:35,080 class has one responsibility. And that it has small 375 00:20:35,080 --> 00:20:38,130 methods. 376 00:20:38,130 --> 00:20:41,180 All the properties of a class should be cohesive 377 00:20:41,180 --> 00:20:44,740 to the abstraction that the class is modeling. If 378 00:20:44,740 --> 00:20:46,500 you have properties that you only use in one 379 00:20:46,500 --> 00:20:50,370 or two methods, that's probably something else that shouldn't 380 00:20:50,370 --> 00:20:53,430 be in there. 381 00:20:53,430 --> 00:20:55,170 Finding a good name for a class will also 382 00:20:55,170 --> 00:20:59,030 help us keep it focused on a single responsibility. 383 00:20:59,030 --> 00:21:01,480 I sometimes talk to the class. Have you ever 384 00:21:01,480 --> 00:21:03,870 heard the concept of talking to the rubber duck? 385 00:21:03,870 --> 00:21:05,770 Or just explaining your problem to someone? They don't 386 00:21:05,770 --> 00:21:08,220 even have to respond, and it helps you figure 387 00:21:08,220 --> 00:21:09,180 it out. 388 00:21:09,180 --> 00:21:11,060 Sometimes I just ask my class, hey class, what 389 00:21:11,060 --> 00:21:13,610 do you do? And if it comes out with 390 00:21:13,610 --> 00:21:16,300 a long list, you've got a problem. 391 00:21:16,300 --> 00:21:18,540 So, the main tools we're gonna use to create 392 00:21:18,540 --> 00:21:23,520 new classes from existing code, not from scratch, but 393 00:21:23,520 --> 00:21:26,720 from existing code, is the extract class and move 394 00:21:26,720 --> 00:21:29,950 method refactorings, which we're gonna go through here. 395 00:21:29,950 --> 00:21:35,810 So, those characteristics of well-designed class. Single responsibility. Cohesive 396 00:21:35,810 --> 00:21:38,860 around a set of properties. Additionally, it has a 397 00:21:38,860 --> 00:21:43,460 small public interface that, preferably, handles a handful of 398 00:21:43,460 --> 00:21:46,550 methods at the most. That it implements a single 399 00:21:46,550 --> 00:21:49,770 use-case, if possible, and that the primary logic is 400 00:21:49,770 --> 00:21:52,610 expressed in a composed method. 401 00:21:52,610 --> 00:21:53,870 That last one, I'm not gonna be covering the 402 00:21:53,870 --> 00:21:56,870 composed method. That's a whole nother talk. But you 403 00:21:56,870 --> 00:21:59,390 should check that practice out. It can really clarify 404 00:21:59,390 --> 00:22:02,650 code and make it much, much more understandable. 405 00:22:02,650 --> 00:22:04,690 So, let's look at the code we should have 406 00:22:04,690 --> 00:22:06,970 been driving towards as soon as the string option 407 00:22:06,970 --> 00:22:10,650 type showed up. We're gonna imagine right now that 408 00:22:10,650 --> 00:22:12,870 we have a string sheet, and we can write 409 00:22:12,870 --> 00:22:16,320 CommandLineOptions the way we would have with the knowledge 410 00:22:16,320 --> 00:22:19,250 that we have now. 411 00:22:19,250 --> 00:22:23,230 That needs to support boolean, string, and integer options. 412 00:22:23,230 --> 00:22:26,850 And remember, we have our tests at our back, 413 00:22:26,850 --> 00:22:30,220 making sure that we don't break anything. 414 00:22:30,220 --> 00:22:33,720 And, here was my first take at it on 415 00:22:33,720 --> 00:22:38,090 what I'd write. The class is twenty-eight lines long. 416 00:22:38,090 --> 00:22:40,800 It is cohesive around the properties. When we're done, 417 00:22:40,800 --> 00:22:43,190 most of the methods are gonna deal with the, 418 00:22:43,190 --> 00:22:46,830 the hash of options and the array of args. 419 00:22:46,830 --> 00:22:51,270 It has a single primary responsibility. Manage a collection 420 00:22:51,270 --> 00:22:54,180 of option objects. 421 00:22:54,180 --> 00:22:57,600 So now we've introduced a collaborator. It also manufactures 422 00:22:57,600 --> 00:23:00,750 the option objects, which I could abstract to another 423 00:23:00,750 --> 00:23:02,970 class. But for the moment, I'm gonna leave it. 424 00:23:02,970 --> 00:23:05,570 If I find it hurts in the future, then 425 00:23:05,570 --> 00:23:09,330 I'll change it. That's my general rule. My guideline. 426 00:23:09,330 --> 00:23:12,790 Is I refactor when it hurts. When making a 427 00:23:12,790 --> 00:23:15,520 change hurts, that's the time to refactor. 428 00:23:15,520 --> 00:23:20,250 My CommandLineOptions class has a small public interface. Just 429 00:23:20,250 --> 00:23:23,220 two methods, valid? and value. And it has no 430 00:23:23,220 --> 00:23:27,000 hard-coded external dependencies yet. I could mess that up 431 00:23:27,000 --> 00:23:29,960 and introduce those, but we're gonna avoid that. 432 00:23:29,960 --> 00:23:32,490 Another interesting characteristic is that, is that there are 433 00:23:32,490 --> 00:23:36,450 no conditional statements in this class, and we're gonna 434 00:23:36,450 --> 00:23:41,200 keep it that way. In Sandi Metz's 2009 Gerupo?? 435 00:23:41,200 --> 00:23:44,760 talk, on the Solid Principles, she said something along 436 00:23:44,760 --> 00:23:48,170 the lines of, a conditional in an OO language 437 00:23:48,170 --> 00:23:49,920 is a smell. 438 00:23:49,920 --> 00:23:53,820 And that's a really powerful statement. I don't think 439 00:23:53,820 --> 00:23:56,550 Sandi's saying that we can't use conditionals in our 440 00:23:56,550 --> 00:24:01,880 code, but that we use conditionals to hide abstractions. 441 00:24:01,880 --> 00:24:05,230 To hide our ducks. 442 00:24:05,230 --> 00:24:06,610 The first time I saw that talk, I don't 443 00:24:06,610 --> 00:24:08,020 even know if I heard her say it. It 444 00:24:08,020 --> 00:24:11,050 was when I went back and rewatched it. I 445 00:24:11,050 --> 00:24:14,210 thought, really? Then, as the years have gone on 446 00:24:14,210 --> 00:24:16,400 and I've been working, I've gotten to the point 447 00:24:16,400 --> 00:24:18,540 where I agree with her. 448 00:24:18,540 --> 00:24:20,960 If you have a lot of conditionals in a 449 00:24:20,960 --> 00:24:24,420 class, you have probably missed a concept that should 450 00:24:24,420 --> 00:24:28,850 be abstracted out of it. 451 00:24:28,850 --> 00:24:31,710 So the initialize and option method from our previous 452 00:24:31,710 --> 00:24:35,230 implementation carry over unchanged. Except that we're gonna store 453 00:24:35,230 --> 00:24:37,270 the options in a hash instead of just the 454 00:24:37,270 --> 00:24:39,690 type. 455 00:24:39,690 --> 00:24:42,080 My valid? method now simply asks all the options 456 00:24:42,080 --> 00:24:45,250 if they're valid, and the value method simply looks 457 00:24:45,250 --> 00:24:48,250 up the option hash and asks it for its 458 00:24:48,250 --> 00:24:51,580 value. So, now we need to build the options. 459 00:24:51,580 --> 00:24:53,830 We have to implement this. And this is where 460 00:24:53,830 --> 00:24:56,760 we're gonna instantiate the objects that represent the boolean, 461 00:24:56,760 --> 00:24:59,300 string, and integer options. 462 00:24:59,300 --> 00:25:04,340 So, now we have the CommandLineOption class, we need 463 00:25:04,340 --> 00:25:07,160 collaborators. In order to get anything done, CommandLineOption needs 464 00:25:07,160 --> 00:25:10,970 option classes to manage. It's gonna have those objects. 465 00:25:10,970 --> 00:25:13,670 So this is creating a dependency. And if we're 466 00:25:13,670 --> 00:25:15,760 gonna create a dependency in our code, we can 467 00:25:15,760 --> 00:25:18,020 do it in a way that's amenable to change, 468 00:25:18,020 --> 00:25:18,860 or we can do it in a way that's 469 00:25:18,860 --> 00:25:25,500 gonna make it hurt in the future. 470 00:25:25,500 --> 00:25:28,020 You don't want to depend, or, excuse me, you 471 00:25:28,020 --> 00:25:33,020 want to depend on abstractions, ot concretions. Depend on 472 00:25:33,020 --> 00:25:36,500 the duck type, not the concrete type. In our 473 00:25:36,500 --> 00:25:39,380 case, depend on the concept, the concept of an 474 00:25:39,380 --> 00:25:43,770 option. Not on the concrete types that implement that 475 00:25:43,770 --> 00:25:46,630 abstraction. 476 00:25:46,630 --> 00:25:49,900 In our case, option is the duck type. This 477 00:25:49,900 --> 00:25:52,340 is the abstraction that I missed earlier, when I 478 00:25:52,340 --> 00:25:57,310 just kept going down the conditional logic path. 479 00:25:57,310 --> 00:26:00,420 It's really simple. It has a valid? method and 480 00:26:00,420 --> 00:26:07,380 a value method. String option, integer option, and boolean 481 00:26:07,380 --> 00:26:11,450 option, those are the concrete implementation of the option 482 00:26:11,450 --> 00:26:15,080 abstraction. All they need is a valid? and a 483 00:26:15,080 --> 00:26:18,890 value method, and a consistent method of construction, and 484 00:26:18,890 --> 00:26:24,720 I can depend on the abstraction, not on the 485 00:26:24,720 --> 00:26:26,260 concretions. 486 00:26:26,260 --> 00:26:32,280 So, how do I do that? I could go 487 00:26:32,280 --> 00:26:34,830 down the case statement road again and check the 488 00:26:34,830 --> 00:26:37,980 option type, instantiating the correct type of the option 489 00:26:37,980 --> 00:26:40,970 based upon the symbol. But I'm not gonna do 490 00:26:40,970 --> 00:26:44,200 that, cause that would tie CommandLineClass to those concrete 491 00:26:44,200 --> 00:26:47,000 types, which is what we're trying to avoid. 492 00:26:47,000 --> 00:26:50,250 That creates a hard dependency between CommandLineOptions class and 493 00:26:50,250 --> 00:26:54,880 those various classes. Instead, I'm gonna use the dynamic 494 00:26:54,880 --> 00:26:58,310 capabilities of Ruby to instantiate those objects for us 495 00:26:58,310 --> 00:27:01,130 using naming conventions. For string, we're going to have 496 00:27:01,130 --> 00:27:06,290 a string option. For booleans, boolean option. Et cetera. 497 00:27:06,290 --> 00:27:09,110 I could do this even in many static languages. 498 00:27:09,110 --> 00:27:13,190 So this isn't something that's specific to Ruby. And 499 00:27:13,190 --> 00:27:15,330 this is a very. This very simple change takes 500 00:27:15,330 --> 00:27:19,330 out CommandLineOption class from depending on those concrete implementations 501 00:27:19,330 --> 00:27:23,490 and flips it to depending on the abstraction. 502 00:27:23,490 --> 00:27:27,510 This is dependency inversion from the Solid Principles, in 503 00:27:27,510 --> 00:27:33,430 practice. Alternately, some other people have suggested, you could 504 00:27:33,430 --> 00:27:36,190 use a hash and map from the string, boolean, 505 00:27:36,190 --> 00:27:39,230 and integer symbols to the concrete classes, kind of 506 00:27:39,230 --> 00:27:41,460 like what Sandi did in her Gilded Rose Coda?? 507 00:27:41,460 --> 00:27:44,320 solution earlier. 508 00:27:44,320 --> 00:27:47,020 That's OK. But, it is an additional thing that 509 00:27:47,020 --> 00:27:50,910 I have to maintain over time. It's a reason 510 00:27:50,910 --> 00:27:54,900 to open the CommandLineOptions and change it if I 511 00:27:54,900 --> 00:27:58,420 have to add a new type of option. If 512 00:27:58,420 --> 00:28:00,460 using the dynamic ability of Ruby bothers you, then 513 00:28:00,460 --> 00:28:03,420 make a hash. Personally, I'm fine with using the 514 00:28:03,420 --> 00:28:06,100 dynamic capabilities of my language. 515 00:28:06,100 --> 00:28:10,780 So, in my case, I've inoculated CommandLineOptions class from 516 00:28:10,780 --> 00:28:14,340 needing to change to support new option types. And 517 00:28:14,340 --> 00:28:16,540 at this point, this class should be closed for 518 00:28:16,540 --> 00:28:20,980 modification, but open for extension. 519 00:28:20,980 --> 00:28:23,360 So, now we need to move the logic for 520 00:28:23,360 --> 00:28:27,340 the various option types to the appropriate option classes. 521 00:28:27,340 --> 00:28:29,400 I decided to make a base class of option 522 00:28:29,400 --> 00:28:32,030 for my concrete types to inherit from, because the 523 00:28:32,030 --> 00:28:34,140 manner of initialization needs to be the same for 524 00:28:34,140 --> 00:28:38,000 all of them. No sense of repeating that code. 525 00:28:38,000 --> 00:28:40,730 And the subtypes have a cohesion around the flag 526 00:28:40,730 --> 00:28:43,090 attribute, and the wrong, excuse me, the flag and 527 00:28:43,090 --> 00:28:47,590 the raw value properties that in the code. 528 00:28:47,590 --> 00:28:50,030 Here's the boolean option. This one I just wrote 529 00:28:50,030 --> 00:28:53,040 because the requirements are so simple. Booleans are always 530 00:28:53,040 --> 00:28:55,460 valid, and they just return the raw_value from the 531 00:28:55,460 --> 00:28:57,290 command line. If it's present, it's truthy. If it's 532 00:28:57,290 --> 00:29:01,560 nil it's falsey. Very simple. 533 00:29:01,560 --> 00:29:03,190 But now we need to implement string option and 534 00:29:03,190 --> 00:29:06,200 integer option. And the logic for their validation and 535 00:29:06,200 --> 00:29:11,390 value extraction is in the old CommandLineOptions class. So, 536 00:29:11,390 --> 00:29:13,870 on the left are the original CommandLineOptions' valid? and 537 00:29:13,870 --> 00:29:17,230 value methods. On the right are those new string 538 00:29:17,230 --> 00:29:19,120 option and integer option classes. 539 00:29:19,120 --> 00:29:22,250 As you can see, the process of creating the 540 00:29:22,250 --> 00:29:25,830 option class was simply picking apart and disassembling the 541 00:29:25,830 --> 00:29:29,590 old command line option class. Moving the logic to 542 00:29:29,590 --> 00:29:33,790 where it belongs, using a combination of extract class 543 00:29:33,790 --> 00:29:37,820 and move method refactorings, we've really cleaned up the 544 00:29:37,820 --> 00:29:39,520 command option, CommandLineOptions. 545 00:29:39,520 --> 00:29:45,190 Frankly, there's not much code left there anymore. So, 546 00:29:45,190 --> 00:29:48,100 now we can replace that nasty, hard to understand 547 00:29:48,100 --> 00:29:53,340 valid? method with this. And the large value method 548 00:29:53,340 --> 00:29:57,510 with this. 549 00:29:57,510 --> 00:30:03,130 To create the specs for the various option classes, 550 00:30:03,130 --> 00:30:05,940 I moved the corresponding section from the CommandLineOptions spec 551 00:30:05,940 --> 00:30:08,460 to the corresponding area for the particular type of 552 00:30:08,460 --> 00:30:12,040 option, and then lightly reworked them and then I 553 00:30:12,040 --> 00:30:14,120 worked them from red to green, as I went 554 00:30:14,120 --> 00:30:17,440 through the process of extracting those classes and moving 555 00:30:17,440 --> 00:30:22,970 the code to those methods. 556 00:30:22,970 --> 00:30:25,540 We've isolated abstractions here. And how do we do 557 00:30:25,540 --> 00:30:30,200 that? We separate the what from the how, like 558 00:30:30,200 --> 00:30:35,090 we've done in CommandLineOptions. We want to move from 559 00:30:35,090 --> 00:30:39,130 code that looks like this to code that looks 560 00:30:39,130 --> 00:30:43,610 like this. 561 00:30:43,610 --> 00:30:48,190 The original CommandLineOptions' valid? method contained all of the 562 00:30:48,190 --> 00:30:51,600 how. The refactored valid? method says what we want 563 00:30:51,600 --> 00:30:55,230 done for us. That's it. All of the how 564 00:30:55,230 --> 00:30:58,030 has moved to the collaborators of our main class, 565 00:30:58,030 --> 00:31:02,330 in this case, StringOption, boolean option, and IntegerOption. 566 00:31:02,330 --> 00:31:06,220 We want to move from that looks like this 567 00:31:06,220 --> 00:31:08,860 to code that looks like this. Move the nitty 568 00:31:08,860 --> 00:31:12,220 gritty details of your code out to the leaves 569 00:31:12,220 --> 00:31:16,470 of your system. And let the center be a 570 00:31:16,470 --> 00:31:17,390 coordinator. 571 00:31:17,390 --> 00:31:21,320 So, when we're done with this, this is what 572 00:31:21,320 --> 00:31:25,580 our CommandLineOptions class looks like. These are our public 573 00:31:25,580 --> 00:31:30,010 methods. It provides a very small surface. And it 574 00:31:30,010 --> 00:31:32,730 fulfills the use case. And these are the private 575 00:31:32,730 --> 00:31:35,900 implementation cruft. It's necessary, but no one really needs 576 00:31:35,900 --> 00:31:37,480 to go poking around in here, and I've made 577 00:31:37,480 --> 00:31:41,360 it obvious by declaring these methods private. 578 00:31:41,360 --> 00:31:44,350 They're for me. Not for you. 579 00:31:44,350 --> 00:31:46,740 So in the end, the sum total of the 580 00:31:46,740 --> 00:31:50,360 implementation of the public interface, and it's all delegated. 581 00:31:50,360 --> 00:31:54,330 All delegated. 582 00:31:54,330 --> 00:31:56,040 So in the process of making the specs pass, 583 00:31:56,040 --> 00:31:58,850 I commented out that dreamed up code as I 584 00:31:58,850 --> 00:32:01,280 went through the process, and then one by one 585 00:32:01,280 --> 00:32:04,150 I wrote the examples and uncommented the code and 586 00:32:04,150 --> 00:32:09,250 made them pass, working from red to green. 587 00:32:09,250 --> 00:32:16,250 Then, because nothing is ever really done, my buddy 588 00:32:17,000 --> 00:32:20,880 says, hey. Any chance you could add the ability 589 00:32:20,880 --> 00:32:22,740 for me to pass an array of values for 590 00:32:22,740 --> 00:32:24,160 an option? 591 00:32:24,160 --> 00:32:28,320 So, to implement this new requirement, I only need 592 00:32:28,320 --> 00:32:31,640 the new array option class. So I write a 593 00:32:31,640 --> 00:32:37,780 spec example. Make it fail. Then create the ArrayOption 594 00:32:37,780 --> 00:32:43,260 class, and I'm done. And this particular example, my 595 00:32:43,260 --> 00:32:48,600 OptionClass is inheriting from the OptionWithContent superclass. And, cause 596 00:32:48,600 --> 00:32:50,650 I actually went through this and realized that strings, 597 00:32:50,650 --> 00:32:54,200 integers, and arrays all have content, so I abstracted 598 00:32:54,200 --> 00:32:56,390 that superclass and, in this case, all I have 599 00:32:56,390 --> 00:32:58,960 to do is write the value method of that 600 00:32:58,960 --> 00:33:03,330 particular type and I'm done. 601 00:33:03,330 --> 00:33:07,200 And it works. 602 00:33:07,200 --> 00:33:12,820 So, we now have a CommandLineOption class that's closed 603 00:33:12,820 --> 00:33:17,520 for modification, but open for extension. I could all 604 00:33:17,520 --> 00:33:22,350 float types, decimal types, other types of options, and 605 00:33:22,350 --> 00:33:24,230 I don't have to go back and touch that 606 00:33:24,230 --> 00:33:25,920 class again. 607 00:33:25,920 --> 00:33:29,390 We have small, easy to understand option classes that 608 00:33:29,390 --> 00:33:36,390 have a single responsibility. Oops, excuse me. 609 00:33:43,560 --> 00:33:46,910 We can. So, we have a easy to understand 610 00:33:46,910 --> 00:33:50,120 option classes that have a single responsibility, and easy 611 00:33:50,120 --> 00:33:54,100 to compose together with that CommandLineOption class. And we 612 00:33:54,100 --> 00:33:56,350 can simply create new option types and have them 613 00:33:56,350 --> 00:34:00,890 instantiated by convention. 614 00:34:00,890 --> 00:34:04,210 My name is Mark Menard. My company's Enable Labs. 615 00:34:04,210 --> 00:34:07,030 We do full lifecycle business productivity and sass app 616 00:34:07,030 --> 00:34:10,329 development, from napkin to production, as I say. And, 617 00:34:10,329 --> 00:34:12,079 I'm gonna be around the conference, so let's get 618 00:34:12,079 --> 00:34:13,440 together and talk about some code. 619 00:34:13,440 --> 00:34:14,668 And we can do some questions.