0:00:17.300,0:00:19.410 MARK MENARD: So, many thanks to the organizers 0:00:19.410,0:00:21.349 here at RailsConf. This is my first time 0:00:21.349,0:00:23.890 talking at RailsConf. It's, frankly, 0:00:23.890,0:00:25.250 kind of intimidating to be up here 0:00:25.250,0:00:27.750 and see so many people out there. 0:00:27.750,0:00:29.550 My name is Mark Menard. I'm gonna be talking 0:00:29.550,0:00:32.619 about small code today. And I've got a lot 0:00:32.619,0:00:35.159 of code. About seventy-nine slides. A hundred[br]and thirty-seven 0:00:35.159,0:00:37.940 transitions. Not quite as much as Sandi had,[br]but 0:00:37.940,0:00:40.480 it's a lot to get through. So let's get 0:00:40.480,0:00:41.680 going. 0:00:41.680,0:00:44.250 So, I'm just gonna let this quote up there 0:00:44.250,0:00:44.809 sink in. 0:00:44.809,0:00:51.809 So. All of us have that file filled with 0:00:53.250,0:00:56.609 code that you just don't want to open. As 0:00:56.609,0:01:00.559 you heard earlier, maybe it's your User class.[br]That 0:01:00.559,0:01:02.539 class that has comments like, woe to ye who 0:01:02.539,0:01:05.880 edit here. The problem with this code is that 0:01:05.880,0:01:10.860 it does live forever. It encapsulates business[br]logic that 0:01:10.860,0:01:13.039 ends up getting duplicated elsewhere, cause[br]no one wants 0:01:13.039,0:01:15.450 to go in there and look at that code. 0:01:15.450,0:01:17.520 It's also very hard to understand. 0:01:17.520,0:01:19.970 I'm gonna be talking about ways to avoid this 0:01:19.970,0:01:24.580 situation. I'm gonna be talking about code.[br]Code at 0:01:24.580,0:01:28.000 the class level and the method level. Having[br]small 0:01:28.000,0:01:31.020 code at the class and method level is fundamental 0:01:31.020,0:01:34.069 to being able to create systems that are composed, 0:01:34.069,0:01:37.599 composed of small, understandable parts. 0:01:37.599,0:01:39.110 I'm gonna lay out a few base concepts so 0:01:39.110,0:01:41.739 that we can start with a clean sheet and 0:01:41.739,0:01:44.340 on the same page. I think there's a lot 0:01:44.340,0:01:46.420 of problems with what people conceive of as[br]small 0:01:46.420,0:01:49.179 or well-designed code. 0:01:49.179,0:01:50.929 It's not about the actual amount of code you 0:01:50.929,0:01:54.640 write, but how the code is organized and the 0:01:54.640,0:01:59.220 size of the units of code. Fundamentally,[br]writing small 0:01:59.220,0:02:01.890 code is really design discipline, because[br]the only way 0:02:01.890,0:02:04.729 you can write small code is use good design 0:02:04.729,0:02:07.569 and refactoring. 0:02:07.569,0:02:09.840 Design and refactoring the way we write small[br]code. 0:02:09.840,0:02:12.250 You can't just sit down and write small code, 0:02:12.250,0:02:14.750 perfectly well-designed code on the first[br]draft. It doesn't 0:02:14.750,0:02:18.450 work that way. It's iterative process. 0:02:18.450,0:02:23.260 So what do I mean by small? It's not 0:02:23.260,0:02:27.610 about total line count. Well-designed code[br]will typically have 0:02:27.610,0:02:30.750 more lines of code than bad code. Just the 0:02:30.750,0:02:33.700 overhead of declaring methods and classes[br]is gonna increase 0:02:33.700,0:02:36.230 your line count. 0:02:36.230,0:02:39.480 It's not about method count. Well-factored[br]code's gonna have 0:02:39.480,0:02:46.480 more, smaller methods. It's not about class[br]count. Well-designed 0:02:47.500,0:02:51.240 code is almost definitely going to have more[br]classes 0:02:51.240,0:02:53.380 than what I call undesigned code. 0:02:53.380,0:02:57.530 Although I've seen some cases of over-abstraction,[br]I find 0:02:57.530,0:03:01.390 that's pretty rare unless someone goes pattern[br]crazy. So 0:03:01.390,0:03:03.360 small code is definitely not about decreasing[br]the number 0:03:03.360,0:03:08.460 of classes in your system. It's about well-designed,[br]it's 0:03:08.460,0:03:12.340 about well-designed classes that aren't poorly[br]designed. 0:03:12.340,0:03:14.460 So what do I mean by small? Small methods, 0:03:14.460,0:03:17.290 small classes. Small methods are the foundation[br]of writing 0:03:17.290,0:03:21.450 small code. Without the ability to decompose[br]large methods 0:03:21.450,0:03:25.180 into small methods, we cannot write small[br]code. And 0:03:25.180,0:03:28.180 without small methods, we can't raise the[br]level of 0:03:28.180,0:03:30.740 abstraction. 0:03:30.740,0:03:32.709 To write small code, we have to be able 0:03:32.709,0:03:36.350 to decompose large classes into smaller classes,[br]and abstract 0:03:36.350,0:03:40.400 responsibilities out of them and separate[br]them on higher-level, 0:03:40.400,0:03:44.180 and base them on higher-level abstractions. 0:03:44.180,0:03:46.459 It's important that our classes are small,[br]because small 0:03:46.459,0:03:52.530 classes are what lead to reusability and composability. 0:03:52.530,0:03:59.530 So, why should we strive for small code? Why 0:04:00.640,0:04:05.020 is it important? 0:04:05.020,0:04:08.880 We don't know what the future is going to 0:04:08.880,0:04:15.680 bring. Your software requirements are going[br]to change. Software 0:04:15.680,0:04:19.339 must be amenable to change. Any system of[br]software 0:04:19.339,0:04:23.370 that's going to have a long, successful life,[br]is 0:04:23.370,0:04:28.250 going to change significantly. Small code[br]is simply easier 0:04:28.250,0:04:32.160 to work with than large, complex code. If[br]the 0:04:32.160,0:04:34.990 requirements of your software are never gonna[br]change, you 0:04:34.990,0:04:38.090 can ignore everything that I have to say here. 0:04:38.090,0:04:45.090 But I doubt that that's the case. 0:04:45.409,0:04:47.729 We should write small code because it helps[br]us 0:04:47.729,0:04:52.199 raise the level of abstraction in our code.[br]It's 0:04:52.199,0:04:54.029 one of the most important things we do to 0:04:54.029,0:05:00.289 create readable, understandable code. All[br]good design is really 0:05:00.289,0:05:05.340 driving toward expressing ubiquitous language[br]of our problem domain 0:05:05.340,0:05:07.960 in our code. 0:05:07.960,0:05:10.879 The combination of small methods and small[br]classes is 0:05:10.879,0:05:13.870 going to help us raise that level of abstraction 0:05:13.870,0:05:20.870 and express those higher-level domain concepts. 0:05:21.229,0:05:22.870 We should also write small code so we can 0:05:22.870,0:05:27.960 effectively use composition. Small classes[br]and small methods compose 0:05:27.960,0:05:31.819 together well. As we compose instances of[br]small objects 0:05:31.819,0:05:36.930 together, our systems will become message-based.[br]In order to 0:05:36.930,0:05:39.909 build systems that are message-based, we have[br]to use 0:05:39.909,0:05:44.569 delegation. And small, composable parts. Small[br]code makes small 0:05:44.569,0:05:47.779 composable parts. It's gonna help our software[br]have flexibility 0:05:47.779,0:05:50.110 and lead to a suppleness over time, and allow 0:05:50.110,0:05:52.900 us to follow those messages. And eventually[br]we're gonna 0:05:52.900,0:05:57.699 see, find our duck types. 0:05:57.699,0:06:01.430 And all this is about enabling future change.[br]And 0:06:01.430,0:06:06.509 accommodate the future requirements without[br]a forklift replacement. 0:06:06.509,0:06:12.490 So the goal: small units of understandable[br]code that 0:06:12.490,0:06:18.099 are amenable to change. 0:06:18.099,0:06:22.300 Our primary tools are extract method and extract[br]class. 0:06:22.300,0:06:25.349 Longer methods are harder to understand than[br]short methods. 0:06:25.349,0:06:26.719 And most of the time, we can shorten a 0:06:26.719,0:06:30.869 method simply by using the abstract method[br]refactoring. I 0:06:30.869,0:06:35.069 use this thing all the time when I'm coding. 0:06:35.069,0:06:36.240 And once we have a set of methods that 0:06:36.240,0:06:39.680 are coherent around a concept, then we can[br]look 0:06:39.680,0:06:43.059 to abstract those into a separate class and[br]move 0:06:43.059,0:06:47.289 the methods to that new class. 0:06:47.289,0:06:50.129 So, I'm gonna be using the example of a 0:06:50.129,0:06:53.680 command line option parser that handles booleans[br]to start 0:06:53.680,0:06:55.650 with, and then we're gonna see where the future 0:06:55.650,0:06:57.020 takes us. 0:06:57.020,0:07:01.770 So, with the command line, I want to be 0:07:01.770,0:07:05.749 able to run some Ruby program dash v. And 0:07:05.749,0:07:11.580 handle boolean options. That's where we're[br]gonna start. 0:07:11.580,0:07:13.550 In my Ruby program, I want to define what 0:07:13.550,0:07:20.550 options I'm looking for, using this simple[br]DSL. And 0:07:21.330,0:07:22.669 then I want to be able to consume it 0:07:22.669,0:07:26.839 like this. If options.has and then a particular[br]option, 0:07:26.839,0:07:28.180 I do something. 0:07:28.180,0:07:34.499 Putting it all together. The DSL, the program[br]at 0:07:34.499,0:07:36.389 the top, the DSL, and then how we actually 0:07:36.389,0:07:42.749 consume that options object. Pretty simple. 0:07:42.749,0:07:47.939 Here's my spec. It's pretty simple. It's true[br]if 0:07:47.939,0:07:49.899 the option is defined and it's present on[br]the 0:07:49.899,0:07:54.300 command line. And it's false if it's not. 0:07:54.300,0:07:56.469 So I run my specs and I get two 0:07:56.469,0:08:03.469 failures. Yes, I used TDD. So, here's my implementation 0:08:04.719,0:08:09.439 that fits on one slide. Pretty simply, I store 0:08:09.439,0:08:12.550 the defined options in an array, and I store 0:08:12.550,0:08:17.339 the arguments, the argv for later reference.[br]Then I 0:08:17.339,0:08:18.800 have a has method that checks to see if 0:08:18.800,0:08:20.860 the option is defined. If it's present in[br]the 0:08:20.860,0:08:21.629 argv. 0:08:21.629,0:08:25.270 And then I've got my option method, which[br]implements 0:08:25.270,0:08:30.770 my simple DSL. Nice and readable. Fits on[br]one 0:08:30.770,0:08:35.440 slide. Probably very comprehendable. 0:08:35.440,0:08:40.360 So I run my tests. Zero failures. They pass. 0:08:40.360,0:08:45.649 I'm done. I get to go home until the 0:08:45.649,0:08:48.660 future comes along. And my workmate comes[br]along and 0:08:48.660,0:08:51.500 says, hey, I really like that library. But,[br]could 0:08:51.500,0:08:54.500 we handle string options? 0:08:54.500,0:08:59.610 Sounds pretty simple. Pretty straightforward.[br]So I think about 0:08:59.610,0:09:01.110 that, and I come up with a small extension 0:09:01.110,0:09:04.110 to the DSL, to just pass a second argument 0:09:04.110,0:09:06.300 as an option with a symbol representation[br]of the 0:09:06.300,0:09:08.019 option type. String, in this case. 0:09:08.019,0:09:10.190 I also default to being a boolean so I 0:09:10.190,0:09:12.110 don't have to change the code that other people 0:09:12.110,0:09:15.149 have done. 0:09:15.149,0:09:18.639 So, a string option. It's a little different[br]than 0:09:18.639,0:09:23.310 a boolean. It actually requires content. So[br]now I 0:09:23.310,0:09:26.519 need the concept of validation. If the string[br]option 0:09:26.519,0:09:28.430 is missing the content, it's not valid. There's[br]no 0:09:28.430,0:09:30.589 string there. 0:09:30.589,0:09:33.430 So, then I'm gonna normalize how I get the 0:09:33.430,0:09:35.519 values out of both those string options and[br]those 0:09:35.519,0:09:38.300 boolean options. You know, that value. This[br]is gonna 0:09:38.300,0:09:40.880 change the API, but sometimes you actually[br]need to 0:09:40.880,0:09:43.750 break the API to enable the future. 0:09:43.750,0:09:46.690 And I'm doing it pretty early. I've only got 0:09:46.690,0:09:50.970 one guy in my office using the library at 0:09:50.970,0:09:53.209 the moment. So, again, putting it all together.[br]I 0:09:53.209,0:09:55.329 can pass the options on the command line.[br]I 0:09:55.329,0:09:58.050 define the options with the DSL, and here's[br]how 0:09:58.050,0:10:00.360 I use my valid? and my value methods to 0:10:00.360,0:10:02.680 find out, get, find out if it's valued and 0:10:02.680,0:10:05.639 get my values out. 0:10:05.639,0:10:09.959 So, now here's the class that implements it.[br]Again, 0:10:09.959,0:10:12.949 on one slide. Probably not as readable. Probably[br]not 0:10:12.949,0:10:16.399 as comprehensible. We're going down what I[br]call kind 0:10:16.399,0:10:20.240 of the undesigned path. It's not too big.[br]Thirty-one 0:10:20.240,0:10:22.089 lines. But it's got issues. 0:10:22.089,0:10:24.389 It's got a method that's definitely large.[br]One that's 0:10:24.389,0:10:27.800 looking on the verge of being large. It's[br]got, 0:10:27.800,0:10:29.980 for only handling booleans and strings, it[br]has quite 0:10:29.980,0:10:34.300 a bit of, of conditional complexity in it[br]already. 0:10:34.300,0:10:36.110 And as we're soon gonna see, it's not very 0:10:36.110,0:10:37.690 amenable to change. 0:10:37.690,0:10:39.639 So we'll look at the pieces and how they 0:10:39.639,0:10:40.850 work, just so yo understand it. 0:10:40.850,0:10:42.839 That's my initialize method. It creates a[br]hash to 0:10:42.839,0:10:44.819 store the options. Because we have to store[br]the 0:10:44.819,0:10:47.430 type now, not just that we have an option. 0:10:47.430,0:10:50.500 It's either boolean or string. And the rest[br]of 0:10:50.500,0:10:54.560 the initialization is the same as it was before. 0:10:54.560,0:10:57.259 And the valid method, we gotta iterate over[br]the 0:10:57.259,0:10:59.949 options, looking to see which ones are strings.[br]So 0:10:59.949,0:11:03.420 we're doing checking on type here. And, and[br]trying 0:11:03.420,0:11:06.190 to see whether they're present and they actually[br]have 0:11:06.190,0:11:06.589 content. 0:11:06.589,0:11:08.720 Currently, string options are the only ones[br]that need 0:11:08.720,0:11:12.610 to validate. Boolean options, there's nothing[br]really to validate. 0:11:12.610,0:11:14.680 Either it's there or it's not. No validation.[br]But 0:11:14.680,0:11:15.920 strings, we have to. 0:11:15.920,0:11:18.209 And the value method, it does a lot of 0:11:18.209,0:11:21.410 stuff. Let's just pretend for a moment this[br]method 0:11:21.410,0:11:23.100 is a black box. We're gonna come back to 0:11:23.100,0:11:26.089 it later. Cause this is, by far, the worst 0:11:26.089,0:11:29.279 code in this current example. 0:11:29.279,0:11:34.300 But, everything is spec'd. And all my specs[br]are 0:11:34.300,0:11:36.399 green. 0:11:36.399,0:11:40.120 So let's talk about methods. Cause we've got[br]some 0:11:40.120,0:11:42.420 big ones and we need to clean them up. 0:11:42.420,0:11:47.629 I call it the first rule of method, of 0:11:47.629,0:11:51.589 methods. Do one thing. Do it well. Do only 0:11:51.589,0:11:52.480 one thing. 0:11:52.480,0:11:55.440 Harkens back to that Unix philosophy of tools[br]that 0:11:55.440,0:11:59.290 you string together with standard in, standard[br]out. But 0:11:59.290,0:12:01.149 how do we determine if a method is actually 0:12:01.149,0:12:04.610 only doing one thing? This is where your level 0:12:04.610,0:12:08.000 of abstraction and the abstract, abstractions[br]in your code 0:12:08.000,0:12:10.579 come into play. And you need to develop a 0:12:10.579,0:12:13.399 feel for this over time. That you want one 0:12:13.399,0:12:16.129 level of abstraction per method. 0:12:16.129,0:12:18.629 If all of our statements are the same level 0:12:18.629,0:12:22.529 of abstraction, and they're coherent around[br]a purpose, then 0:12:22.529,0:12:25.810 I consider that to be doing one thing. Doesn't 0:12:25.810,0:12:27.620 mean it has to be one line in a 0:12:27.620,0:12:30.220 method. 0:12:30.220,0:12:32.399 I can't tell you how many times I've looked 0:12:32.399,0:12:36.009 at code and seen a comment on a method 0:12:36.009,0:12:37.730 that was, like, an excellent description of[br]what the 0:12:37.730,0:12:39.839 method did, and if you just took those words, 0:12:39.839,0:12:42.160 bound together, they'd make a fantastic method[br]name. But 0:12:42.160,0:12:45.310 yet the method is named something else that[br]isn't 0:12:45.310,0:12:50.980 that descriptive. So use descriptive names.[br]It's really critical. 0:12:50.980,0:12:53.149 And the fewer arguments, the better. My personal[br]goal 0:12:53.149,0:12:57.670 is zero arguments on methods. One is OK. Two 0:12:57.670,0:12:59.790 or three. That's when I start to think I've 0:12:59.790,0:13:02.589 probably missed an abstraction in my code[br]and I 0:13:02.589,0:13:06.699 should go back and look at it. 0:13:06.699,0:13:09.819 Separate queries from commands. If you query[br]something and 0:13:09.819,0:13:11.519 it looks like a query method and it changes 0:13:11.519,0:13:14.170 the state of your object, it's hard to reason 0:13:14.170,0:13:16.600 about, and people who consume your library[br]will be 0:13:16.600,0:13:20.569 confused by that. So separate those. 0:13:20.569,0:13:24.589 And, don't repeat yourself. I know Sandi talked[br]about 0:13:24.589,0:13:27.810 this earlier, and it does take some judgment[br]to 0:13:27.810,0:13:31.089 know when it is time to remove the repetition. 0:13:31.089,0:13:32.949 But you don't want to leave repetition over[br]the 0:13:32.949,0:13:35.750 long term, because it will come back to bite 0:13:35.750,0:13:37.379 you. 0:13:37.379,0:13:42.009 So, let's look at our methods. We've got repetition 0:13:42.009,0:13:44.069 here. Both valid? and value are digging through[br]the 0:13:44.069,0:13:46.660 argv array to find the options from the command 0:13:46.660,0:13:49.639 line. This is the perfect candidate for an[br]extract 0:13:49.639,0:13:53.779 method, abstraction. Refactoring. 0:13:53.779,0:13:56.910 We have magic constants scattered around,[br]and those are 0:13:56.910,0:14:02.120 a strong indication that we've missed something.[br]An abstraction. 0:14:02.120,0:14:06.139 We're violating some other rules. It's hard[br]to say 0:14:06.139,0:14:11.149 either of these methods is really doing one[br]thing. 0:14:11.149,0:14:13.829 The code is definitely not at the same level 0:14:13.829,0:14:17.930 of abstraction. Values digging, valid? is[br]digging into the 0:14:17.930,0:14:20.759 argv array and value is figuring out different[br]divergent 0:14:20.759,0:14:23.670 types and how to return their values. So we're 0:14:23.670,0:14:25.800 gonna eliminate some of the repetition with[br]the extract 0:14:25.800,0:14:28.629 method refactoring. 0:14:28.629,0:14:33.160 The extract method refactoring entails moving[br]a part of 0:14:33.160,0:14:35.420 a method into a new method, with a descriptive 0:14:35.420,0:14:38.449 name, that's the naming part. And then calling[br]the 0:14:38.449,0:14:39.529 new method. 0:14:39.529,0:14:42.310 This, this refactoring helps us keep the level[br]of 0:14:42.310,0:14:47.819 abstraction consistent in the method we're[br]abstracting from. Here 0:14:47.819,0:14:49.459 we have one expression on a method that's[br]a 0:14:49.459,0:14:52.839 high level of abstraction, and two statements[br]that are 0:14:52.839,0:14:56.529 a low level of abstraction. So we move the 0:14:56.529,0:14:58.509 less abstract code to a new method with a 0:14:58.509,0:15:01.930 descriptive name, and then we call the new[br]method. 0:15:01.930,0:15:04.920 And this results in the old method, method[br]having 0:15:04.920,0:15:09.990 a consistent level of abstraction. So back[br]to our 0:15:09.990,0:15:14.160 CommandLineOptions class, both valid? and[br]value are digging through 0:15:14.160,0:15:16.689 the argv collection to find the option value.[br]So 0:15:16.689,0:15:18.740 we're gonna abstract that code and get the[br]raw 0:15:18.740,0:15:22.110 value out of argv. 0:15:22.110,0:15:23.749 Then we call the method from where the original 0:15:23.749,0:15:27.519 logic was abstracted. Pretty simple. But now[br]the code 0:15:27.519,0:15:30.089 left behind in valid? and value says what[br]I 0:15:30.089,0:15:34.439 want. Not how to do it. 0:15:34.439,0:15:37.759 The how has been moved to the abstracted method, 0:15:37.759,0:15:40.470 raising the level of abstraction just a little[br]bit 0:15:40.470,0:15:44.240 in valid? and value. 0:15:44.240,0:15:47.279 I'm going to do two more abstractions. I've[br]abstracted 0:15:47.279,0:15:51.540 the string option value method and the abstract[br]content 0:15:51.540,0:15:55.389 method. The naming of the abstracted methods[br]is very 0:15:55.389,0:16:00.540 important. They say what they do. 0:16:00.540,0:16:03.339 But overall, I'm not happy with this code.[br]It 0:16:03.339,0:16:06.329 is more explanatory, but it's fairly complex[br]and hard 0:16:06.329,0:16:09.809 to understand. It's also not as small as it 0:16:09.809,0:16:12.629 could be. The methods are large because I[br]missed 0:16:12.629,0:16:17.499 an abstraction. And we're gonna go find that[br]now. 0:16:17.499,0:16:19.730 I'm referencing the option type symbol to[br]see if 0:16:19.730,0:16:24.519 it's a string, which, that's a big smell.[br]Then 0:16:24.519,0:16:26.290 there are the magic constants used to dig[br]into 0:16:26.290,0:16:28.959 the argv element to find the constant within[br]that 0:16:28.959,0:16:34.240 particular string, the substring. If I was[br]confident that 0:16:34.240,0:16:36.709 I'd have no future added requirements for[br]this class, 0:16:36.709,0:16:40.639 I might leave this alone. It works. It's tested. 0:16:40.639,0:16:43.220 Until my buddy comes to me and says, hey, 0:16:43.220,0:16:45.300 I really like that library, but could we handle 0:16:45.300,0:16:48.149 integers now? 0:16:48.149,0:16:50.959 I could keep driving down this undesigned[br]path I've 0:16:50.959,0:16:53.910 been following, and complicate the valid?[br]and value methods 0:16:53.910,0:16:56.420 by switching on the type of the option and 0:16:56.420,0:16:59.790 digging into those argv elements to find the[br]value. 0:16:59.790,0:17:03.689 But, this is our chance to make a break. 0:17:03.689,0:17:07.380 And make our code more amenable to change. 0:17:07.380,0:17:11.329 But, to illustrate the point, I'm gonna show[br]you 0:17:11.329,0:17:14.939 that undesign method, to show you the OO design 0:17:14.939,0:17:18.199 actually matters. So we're gonna look at this. 0:17:18.199,0:17:20.400 This is the undesigned, non OO version of[br]this 0:17:20.400,0:17:23.869 code. Is it horrible? I'll leave that to you 0:17:23.869,0:17:27.119 to decide. Is it small? In my opinion, definitely 0:17:27.119,0:17:30.120 not. It is not small, by any measure. The 0:17:30.120,0:17:32.090 class is growing due to changes in specification.[br]The 0:17:32.090,0:17:36.490 valid? and value methods are being changed[br]in lock 0:17:36.490,0:17:39.170 step. That's a sure sign we've missed an abstraction 0:17:39.170,0:17:41.850 or a duck type. And those methods are getting 0:17:41.850,0:17:45.900 big and complicated. And now they're doing[br]even more 0:17:45.900,0:17:47.340 things. 0:17:47.340,0:17:52.380 And we're just doing booleans, strings, and[br]integers. Not 0:17:52.380,0:17:53.340 that much. 0:17:53.340,0:18:00.270 The code has tests. They all pass. That's[br]good. 0:18:00.270,0:18:03.670 But it's not satisfying. We've got those large[br]methods 0:18:03.670,0:18:07.880 and complex conditional logic. It's time to[br]refactor now. 0:18:07.880,0:18:10.570 To make the change easy. 0:18:10.570,0:18:12.340 And now we've got the tests that are back, 0:18:12.340,0:18:14.680 so we can do it without fear. 0:18:14.680,0:18:18.420 And, I want to call your attention to a 0:18:18.420,0:18:21.740 pattern that clearly emerges when we go down[br]the 0:18:21.740,0:18:25.720 non OO path here. We see checking the option 0:18:25.720,0:18:30.940 type and divergent behavior based on the type.[br]Don't 0:18:30.940,0:18:36.270 reinvent the type system. If you have ducks,[br]let 0:18:36.270,0:18:37.930 them quack. 0:18:37.930,0:18:40.710 In this example, the option types of boolean,[br]string, 0:18:40.710,0:18:44.080 and integer, those are our ducks. And I'll[br]bet 0:18:44.080,0:18:47.830 there's ducks in your code yearning to be[br]free. 0:18:47.830,0:18:51.410 And just a further confirmation that we're[br]dealing with 0:18:51.410,0:18:55.300 an abstraction or a duck, we see the testing 0:18:55.300,0:18:59.190 option type again in the value method. Hidden[br]inside 0:18:59.190,0:19:02.130 the valid? and value method, there's a case[br]statement 0:19:02.130,0:19:04.700 here. It just didn't evolve that way as I 0:19:04.700,0:19:05.560 was writing the code. 0:19:05.560,0:19:08.720 I'm gonna show you that. You're gonna see[br]that 0:19:08.720,0:19:12.840 it's really clear now. 0:19:12.840,0:19:14.650 Now it should be really obvious what the duck 0:19:14.650,0:19:17.280 type is. If you have case statements like[br]this 0:19:17.280,0:19:24.280 in your code, you've missed an abstraction.[br]Here, again, 0:19:24.540,0:19:26.830 we clearly see the duck type. 0:19:26.830,0:19:30.070 Now, I would guess, if I was writing this, 0:19:30.070,0:19:32.420 as soon as I had the string type, I 0:19:32.420,0:19:35.360 would have gone down the OO path. I just 0:19:35.360,0:19:38.690 wanted to illustrate to you what an undesigned,[br]non 0:19:38.690,0:19:41.700 OO mess you can get yourself into if you 0:19:41.700,0:19:45.570 keep riding the horse until it's dead. 0:19:45.570,0:19:47.290 My dad had a saying hanging on his wall 0:19:47.290,0:19:49.930 in his office. When the horse is dead, get 0:19:49.930,0:19:52.060 off. 0:19:52.060,0:19:54.810 But sometimes we don't realize the horse is[br]dead 0:19:54.810,0:19:58.160 and we just keep trying to go. Now it's 0:19:58.160,0:20:01.440 time to take a fresh look at this. So, 0:20:01.440,0:20:04.280 since class is the fundamental organizational[br]unit we have 0:20:04.280,0:20:07.120 to work with, it's time to work at what 0:20:07.120,0:20:10.820 constitutes a good class. Which principles[br]are gonna lead 0:20:10.820,0:20:14.290 us to be able to write small classes. 0:20:14.290,0:20:19.930 So, how do we write small classes? To make 0:20:19.930,0:20:23.540 small classes, I think, and this is not just 0:20:23.540,0:20:25.060 my opinion. It's a lot of peoples' opinion.[br]The 0:20:25.060,0:20:28.710 most important thing we should assure is that[br]our 0:20:28.710,0:20:35.080 class has one responsibility. And that it[br]has small 0:20:35.080,0:20:38.130 methods. 0:20:38.130,0:20:41.180 All the properties of a class should be cohesive 0:20:41.180,0:20:44.740 to the abstraction that the class is modeling.[br]If 0:20:44.740,0:20:46.500 you have properties that you only use in one 0:20:46.500,0:20:50.370 or two methods, that's probably something[br]else that shouldn't 0:20:50.370,0:20:53.430 be in there. 0:20:53.430,0:20:55.170 Finding a good name for a class will also 0:20:55.170,0:20:59.030 help us keep it focused on a single responsibility. 0:20:59.030,0:21:01.480 I sometimes talk to the class. Have you ever 0:21:01.480,0:21:03.870 heard the concept of talking to the rubber[br]duck? 0:21:03.870,0:21:05.770 Or just explaining your problem to someone?[br]They don't 0:21:05.770,0:21:08.220 even have to respond, and it helps you figure 0:21:08.220,0:21:09.180 it out. 0:21:09.180,0:21:11.060 Sometimes I just ask my class, hey class,[br]what 0:21:11.060,0:21:13.610 do you do? And if it comes out with 0:21:13.610,0:21:16.300 a long list, you've got a problem. 0:21:16.300,0:21:18.540 So, the main tools we're gonna use to create 0:21:18.540,0:21:23.520 new classes from existing code, not from scratch,[br]but 0:21:23.520,0:21:26.720 from existing code, is the extract class and[br]move 0:21:26.720,0:21:29.950 method refactorings, which we're gonna go[br]through here. 0:21:29.950,0:21:35.810 So, those characteristics of well-designed[br]class. Single responsibility. Cohesive 0:21:35.810,0:21:38.860 around a set of properties. Additionally,[br]it has a 0:21:38.860,0:21:43.460 small public interface that, preferably, handles[br]a handful of 0:21:43.460,0:21:46.550 methods at the most. That it implements a[br]single 0:21:46.550,0:21:49.770 use-case, if possible, and that the primary[br]logic is 0:21:49.770,0:21:52.610 expressed in a composed method. 0:21:52.610,0:21:53.870 That last one, I'm not gonna be covering the 0:21:53.870,0:21:56.870 composed method. That's a whole nother talk.[br]But you 0:21:56.870,0:21:59.390 should check that practice out. It can really[br]clarify 0:21:59.390,0:22:02.650 code and make it much, much more understandable. 0:22:02.650,0:22:04.690 So, let's look at the code we should have 0:22:04.690,0:22:06.970 been driving towards as soon as the string[br]option 0:22:06.970,0:22:10.650 type showed up. We're gonna imagine right[br]now that 0:22:10.650,0:22:12.870 we have a string sheet, and we can write 0:22:12.870,0:22:16.320 CommandLineOptions the way we would have with[br]the knowledge 0:22:16.320,0:22:19.250 that we have now. 0:22:19.250,0:22:23.230 That needs to support boolean, string, and[br]integer options. 0:22:23.230,0:22:26.850 And remember, we have our tests at our back, 0:22:26.850,0:22:30.220 making sure that we don't break anything. 0:22:30.220,0:22:33.720 And, here was my first take at it on 0:22:33.720,0:22:38.090 what I'd write. The class is twenty-eight[br]lines long. 0:22:38.090,0:22:40.800 It is cohesive around the properties. When[br]we're done, 0:22:40.800,0:22:43.190 most of the methods are gonna deal with the, 0:22:43.190,0:22:46.830 the hash of options and the array of args. 0:22:46.830,0:22:51.270 It has a single primary responsibility. Manage[br]a collection 0:22:51.270,0:22:54.180 of option objects. 0:22:54.180,0:22:57.600 So now we've introduced a collaborator. It[br]also manufactures 0:22:57.600,0:23:00.750 the option objects, which I could abstract[br]to another 0:23:00.750,0:23:02.970 class. But for the moment, I'm gonna leave[br]it. 0:23:02.970,0:23:05.570 If I find it hurts in the future, then 0:23:05.570,0:23:09.330 I'll change it. That's my general rule. My[br]guideline. 0:23:09.330,0:23:12.790 Is I refactor when it hurts. When making a 0:23:12.790,0:23:15.520 change hurts, that's the time to refactor. 0:23:15.520,0:23:20.250 My CommandLineOptions class has a small public[br]interface. Just 0:23:20.250,0:23:23.220 two methods, valid? and value. And it has[br]no 0:23:23.220,0:23:27.000 hard-coded external dependencies yet. I could[br]mess that up 0:23:27.000,0:23:29.960 and introduce those, but we're gonna avoid[br]that. 0:23:29.960,0:23:32.490 Another interesting characteristic is that,[br]is that there are 0:23:32.490,0:23:36.450 no conditional statements in this class, and[br]we're gonna 0:23:36.450,0:23:41.200 keep it that way. In Sandi Metz's 2009 Gerupo?? 0:23:41.200,0:23:44.760 talk, on the Solid Principles, she said something[br]along 0:23:44.760,0:23:48.170 the lines of, a conditional in an OO language 0:23:48.170,0:23:49.920 is a smell. 0:23:49.920,0:23:53.820 And that's a really powerful statement. I[br]don't think 0:23:53.820,0:23:56.550 Sandi's saying that we can't use conditionals[br]in our 0:23:56.550,0:24:01.880 code, but that we use conditionals to hide[br]abstractions. 0:24:01.880,0:24:05.230 To hide our ducks. 0:24:05.230,0:24:06.610 The first time I saw that talk, I don't 0:24:06.610,0:24:08.020 even know if I heard her say it. It 0:24:08.020,0:24:11.050 was when I went back and rewatched it. I 0:24:11.050,0:24:14.210 thought, really? Then, as the years have gone[br]on 0:24:14.210,0:24:16.400 and I've been working, I've gotten to the[br]point 0:24:16.400,0:24:18.540 where I agree with her. 0:24:18.540,0:24:20.960 If you have a lot of conditionals in a 0:24:20.960,0:24:24.420 class, you have probably missed a concept[br]that should 0:24:24.420,0:24:28.850 be abstracted out of it. 0:24:28.850,0:24:31.710 So the initialize and option method from our[br]previous 0:24:31.710,0:24:35.230 implementation carry over unchanged. Except[br]that we're gonna store 0:24:35.230,0:24:37.270 the options in a hash instead of just the 0:24:37.270,0:24:39.690 type. 0:24:39.690,0:24:42.080 My valid? method now simply asks all the options 0:24:42.080,0:24:45.250 if they're valid, and the value method simply[br]looks 0:24:45.250,0:24:48.250 up the option hash and asks it for its 0:24:48.250,0:24:51.580 value. So, now we need to build the options. 0:24:51.580,0:24:53.830 We have to implement this. And this is where 0:24:53.830,0:24:56.760 we're gonna instantiate the objects that represent[br]the boolean, 0:24:56.760,0:24:59.300 string, and integer options. 0:24:59.300,0:25:04.340 So, now we have the CommandLineOption class,[br]we need 0:25:04.340,0:25:07.160 collaborators. In order to get anything done,[br]CommandLineOption needs 0:25:07.160,0:25:10.970 option classes to manage. It's gonna have[br]those objects. 0:25:10.970,0:25:13.670 So this is creating a dependency. And if we're 0:25:13.670,0:25:15.760 gonna create a dependency in our code, we[br]can 0:25:15.760,0:25:18.020 do it in a way that's amenable to change, 0:25:18.020,0:25:18.860 or we can do it in a way that's 0:25:18.860,0:25:25.500 gonna make it hurt in the future. 0:25:25.500,0:25:28.020 You don't want to depend, or, excuse me, you 0:25:28.020,0:25:33.020 want to depend on abstractions, ot concretions.[br]Depend on 0:25:33.020,0:25:36.500 the duck type, not the concrete type. In our 0:25:36.500,0:25:39.380 case, depend on the concept, the concept of[br]an 0:25:39.380,0:25:43.770 option. Not on the concrete types that implement[br]that 0:25:43.770,0:25:46.630 abstraction. 0:25:46.630,0:25:49.900 In our case, option is the duck type. This 0:25:49.900,0:25:52.340 is the abstraction that I missed earlier,[br]when I 0:25:52.340,0:25:57.310 just kept going down the conditional logic[br]path. 0:25:57.310,0:26:00.420 It's really simple. It has a valid? method[br]and 0:26:00.420,0:26:07.380 a value method. String option, integer option,[br]and boolean 0:26:07.380,0:26:11.450 option, those are the concrete implementation[br]of the option 0:26:11.450,0:26:15.080 abstraction. All they need is a valid? and[br]a 0:26:15.080,0:26:18.890 value method, and a consistent method of construction,[br]and 0:26:18.890,0:26:24.720 I can depend on the abstraction, not on the 0:26:24.720,0:26:26.260 concretions. 0:26:26.260,0:26:32.280 So, how do I do that? I could go 0:26:32.280,0:26:34.830 down the case statement road again and check[br]the 0:26:34.830,0:26:37.980 option type, instantiating the correct type[br]of the option 0:26:37.980,0:26:40.970 based upon the symbol. But I'm not gonna do 0:26:40.970,0:26:44.200 that, cause that would tie CommandLineClass[br]to those concrete 0:26:44.200,0:26:47.000 types, which is what we're trying to avoid. 0:26:47.000,0:26:50.250 That creates a hard dependency between CommandLineOptions[br]class and 0:26:50.250,0:26:54.880 those various classes. Instead, I'm gonna[br]use the dynamic 0:26:54.880,0:26:58.310 capabilities of Ruby to instantiate those[br]objects for us 0:26:58.310,0:27:01.130 using naming conventions. For string, we're[br]going to have 0:27:01.130,0:27:06.290 a string option. For booleans, boolean option.[br]Et cetera. 0:27:06.290,0:27:09.110 I could do this even in many static languages. 0:27:09.110,0:27:13.190 So this isn't something that's specific to[br]Ruby. And 0:27:13.190,0:27:15.330 this is a very. This very simple change takes 0:27:15.330,0:27:19.330 out CommandLineOption class from depending[br]on those concrete implementations 0:27:19.330,0:27:23.490 and flips it to depending on the abstraction. 0:27:23.490,0:27:27.510 This is dependency inversion from the Solid[br]Principles, in 0:27:27.510,0:27:33.430 practice. Alternately, some other people have[br]suggested, you could 0:27:33.430,0:27:36.190 use a hash and map from the string, boolean, 0:27:36.190,0:27:39.230 and integer symbols to the concrete classes,[br]kind of 0:27:39.230,0:27:41.460 like what Sandi did in her Gilded Rose Coda?? 0:27:41.460,0:27:44.320 solution earlier. 0:27:44.320,0:27:47.020 That's OK. But, it is an additional thing[br]that 0:27:47.020,0:27:50.910 I have to maintain over time. It's a reason 0:27:50.910,0:27:54.900 to open the CommandLineOptions and change[br]it if I 0:27:54.900,0:27:58.420 have to add a new type of option. If 0:27:58.420,0:28:00.460 using the dynamic ability of Ruby bothers[br]you, then 0:28:00.460,0:28:03.420 make a hash. Personally, I'm fine with using[br]the 0:28:03.420,0:28:06.100 dynamic capabilities of my language. 0:28:06.100,0:28:10.780 So, in my case, I've inoculated CommandLineOptions[br]class from 0:28:10.780,0:28:14.340 needing to change to support new option types.[br]And 0:28:14.340,0:28:16.540 at this point, this class should be closed[br]for 0:28:16.540,0:28:20.980 modification, but open for extension. 0:28:20.980,0:28:23.360 So, now we need to move the logic for 0:28:23.360,0:28:27.340 the various option types to the appropriate[br]option classes. 0:28:27.340,0:28:29.400 I decided to make a base class of option 0:28:29.400,0:28:32.030 for my concrete types to inherit from, because[br]the 0:28:32.030,0:28:34.140 manner of initialization needs to be the same[br]for 0:28:34.140,0:28:38.000 all of them. No sense of repeating that code. 0:28:38.000,0:28:40.730 And the subtypes have a cohesion around the[br]flag 0:28:40.730,0:28:43.090 attribute, and the wrong, excuse me, the flag[br]and 0:28:43.090,0:28:47.590 the raw value properties that in the code. 0:28:47.590,0:28:50.030 Here's the boolean option. This one I just[br]wrote 0:28:50.030,0:28:53.040 because the requirements are so simple. Booleans[br]are always 0:28:53.040,0:28:55.460 valid, and they just return the raw_value[br]from the 0:28:55.460,0:28:57.290 command line. If it's present, it's truthy.[br]If it's 0:28:57.290,0:29:01.560 nil it's falsey. Very simple. 0:29:01.560,0:29:03.190 But now we need to implement string option[br]and 0:29:03.190,0:29:06.200 integer option. And the logic for their validation[br]and 0:29:06.200,0:29:11.390 value extraction is in the old CommandLineOptions[br]class. So, 0:29:11.390,0:29:13.870 on the left are the original CommandLineOptions'[br]valid? and 0:29:13.870,0:29:17.230 value methods. On the right are those new[br]string 0:29:17.230,0:29:19.120 option and integer option classes. 0:29:19.120,0:29:22.250 As you can see, the process of creating the 0:29:22.250,0:29:25.830 option class was simply picking apart and[br]disassembling the 0:29:25.830,0:29:29.590 old command line option class. Moving the[br]logic to 0:29:29.590,0:29:33.790 where it belongs, using a combination of extract[br]class 0:29:33.790,0:29:37.820 and move method refactorings, we've really[br]cleaned up the 0:29:37.820,0:29:39.520 command option, CommandLineOptions. 0:29:39.520,0:29:45.190 Frankly, there's not much code left there[br]anymore. So, 0:29:45.190,0:29:48.100 now we can replace that nasty, hard to understand 0:29:48.100,0:29:53.340 valid? method with this. And the large value[br]method 0:29:53.340,0:29:57.510 with this. 0:29:57.510,0:30:03.130 To create the specs for the various option[br]classes, 0:30:03.130,0:30:05.940 I moved the corresponding section from the[br]CommandLineOptions spec 0:30:05.940,0:30:08.460 to the corresponding area for the particular[br]type of 0:30:08.460,0:30:12.040 option, and then lightly reworked them and[br]then I 0:30:12.040,0:30:14.120 worked them from red to green, as I went 0:30:14.120,0:30:17.440 through the process of extracting those classes[br]and moving 0:30:17.440,0:30:22.970 the code to those methods. 0:30:22.970,0:30:25.540 We've isolated abstractions here. And how[br]do we do 0:30:25.540,0:30:30.200 that? We separate the what from the how, like 0:30:30.200,0:30:35.090 we've done in CommandLineOptions. We want[br]to move from 0:30:35.090,0:30:39.130 code that looks like this to code that looks 0:30:39.130,0:30:43.610 like this. 0:30:43.610,0:30:48.190 The original CommandLineOptions' valid? method[br]contained all of the 0:30:48.190,0:30:51.600 how. The refactored valid? method says what[br]we want 0:30:51.600,0:30:55.230 done for us. That's it. All of the how 0:30:55.230,0:30:58.030 has moved to the collaborators of our main[br]class, 0:30:58.030,0:31:02.330 in this case, StringOption, boolean option,[br]and IntegerOption. 0:31:02.330,0:31:06.220 We want to move from that looks like this 0:31:06.220,0:31:08.860 to code that looks like this. Move the nitty 0:31:08.860,0:31:12.220 gritty details of your code out to the leaves 0:31:12.220,0:31:16.470 of your system. And let the center be a 0:31:16.470,0:31:17.390 coordinator. 0:31:17.390,0:31:21.320 So, when we're done with this, this is what 0:31:21.320,0:31:25.580 our CommandLineOptions class looks like. These[br]are our public 0:31:25.580,0:31:30.010 methods. It provides a very small surface.[br]And it 0:31:30.010,0:31:32.730 fulfills the use case. And these are the private 0:31:32.730,0:31:35.900 implementation cruft. It's necessary, but[br]no one really needs 0:31:35.900,0:31:37.480 to go poking around in here, and I've made 0:31:37.480,0:31:41.360 it obvious by declaring these methods private. 0:31:41.360,0:31:44.350 They're for me. Not for you. 0:31:44.350,0:31:46.740 So in the end, the sum total of the 0:31:46.740,0:31:50.360 implementation of the public interface, and[br]it's all delegated. 0:31:50.360,0:31:54.330 All delegated. 0:31:54.330,0:31:56.040 So in the process of making the specs pass, 0:31:56.040,0:31:58.850 I commented out that dreamed up code as I 0:31:58.850,0:32:01.280 went through the process, and then one by[br]one 0:32:01.280,0:32:04.150 I wrote the examples and uncommented the code[br]and 0:32:04.150,0:32:09.250 made them pass, working from red to green. 0:32:09.250,0:32:16.250 Then, because nothing is ever really done,[br]my buddy 0:32:17.000,0:32:20.880 says, hey. Any chance you could add the ability 0:32:20.880,0:32:22.740 for me to pass an array of values for 0:32:22.740,0:32:24.160 an option? 0:32:24.160,0:32:28.320 So, to implement this new requirement, I only[br]need 0:32:28.320,0:32:31.640 the new array option class. So I write a 0:32:31.640,0:32:37.780 spec example. Make it fail. Then create the[br]ArrayOption 0:32:37.780,0:32:43.260 class, and I'm done. And this particular example,[br]my 0:32:43.260,0:32:48.600 OptionClass is inheriting from the OptionWithContent[br]superclass. And, cause 0:32:48.600,0:32:50.650 I actually went through this and realized[br]that strings, 0:32:50.650,0:32:54.200 integers, and arrays all have content, so[br]I abstracted 0:32:54.200,0:32:56.390 that superclass and, in this case, all I have 0:32:56.390,0:32:58.960 to do is write the value method of that 0:32:58.960,0:33:03.330 particular type and I'm done. 0:33:03.330,0:33:07.200 And it works. 0:33:07.200,0:33:12.820 So, we now have a CommandLineOption class[br]that's closed 0:33:12.820,0:33:17.520 for modification, but open for extension.[br]I could all 0:33:17.520,0:33:22.350 float types, decimal types, other types of[br]options, and 0:33:22.350,0:33:24.230 I don't have to go back and touch that 0:33:24.230,0:33:25.920 class again. 0:33:25.920,0:33:29.390 We have small, easy to understand option classes[br]that 0:33:29.390,0:33:36.390 have a single responsibility. Oops, excuse[br]me. 0:33:43.560,0:33:46.910 We can. So, we have a easy to understand 0:33:46.910,0:33:50.120 option classes that have a single responsibility,[br]and easy 0:33:50.120,0:33:54.100 to compose together with that CommandLineOption[br]class. And we 0:33:54.100,0:33:56.350 can simply create new option types and have[br]them 0:33:56.350,0:34:00.890 instantiated by convention. 0:34:00.890,0:34:04.210 My name is Mark Menard. My company's Enable[br]Labs. 0:34:04.210,0:34:07.030 We do full lifecycle business productivity[br]and sass app 0:34:07.030,0:34:10.329 development, from napkin to production, as[br]I say. And, 0:34:10.329,0:34:12.079 I'm gonna be around the conference, so let's[br]get 0:34:12.079,0:34:13.440 together and talk about some code. 0:34:13.440,0:34:14.668 And we can do some questions.