Return to Video

RailsConf 2014 - All the Little Things by Sandi Metz

  • 0:01 - 0:17
    (jazzy music)
  • 0:17 - 0:20
    Sandi:So you'd think that writing
  • 0:20 - 0:23
    object-oriented code was hard.
  • 0:23 - 0:27
    All you have to do is
    look at our apps, alright?
  • 0:27 - 0:30
    We mean well, and we write
    code that almost always
  • 0:30 - 0:35
    inevitably we eventually come to hate it.
  • 0:35 - 0:37
    And the more I think
    about this, these days,
  • 0:37 - 0:39
    somehow my job is to think
    about how to write better code.
  • 0:39 - 0:43
    And the more I think about
    it, the more I think that
  • 0:43 - 0:47
    all of the problems we cause
    have the same simple solution,
  • 0:47 - 0:49
    and that when people ask me now
  • 0:49 - 0:51
    how to write object-oriented code,
  • 0:51 - 0:55
    I give them one small piece of advice.
  • 0:55 - 0:58
    I say make smaller
    things, that's all it is.
  • 0:58 - 1:01
    Make smaller classes,
    make smaller methods,
  • 1:01 - 1:05
    and let them know as little
    about each other as possible.
  • 1:05 - 1:07
    And lately I've been on a quest.
  • 1:07 - 1:08
    I've had this obsession for
    the last couple of months,
  • 1:08 - 1:10
    and it's been about conditionals.
  • 1:10 - 1:11
    There's a lot of code out there
  • 1:11 - 1:13
    with nasty conditionals in it,
  • 1:13 - 1:16
    and I've been wondering,
    when should I replace
  • 1:16 - 1:18
    conditionals with small objects,
  • 1:18 - 1:20
    and how should I do this,
  • 1:20 - 1:23
    and what would happen to my code if I do?
  • 1:23 - 1:24
    And I was really confident
    in Miami in November
  • 1:24 - 1:27
    and I inflicted this
    obsession upon Jim Weirich,
  • 1:27 - 1:30
    whom some of you probably knew,
  • 1:30 - 1:33
    and he pointed me in the
    direction of the Gilded Rose.
  • 1:33 - 1:35
    Now this is a Kata, it's apparently
  • 1:35 - 1:37
    really well known, but
    I don't get out much,
  • 1:37 - 1:39
    so I had...
    (laughter)
  • 1:39 - 1:41
    never heard of it.
  • 1:41 - 1:44
    And so, it is so famous
    that you can just Google it,
  • 1:44 - 1:47
    and get an explanation of the
    problem, but I didn't do that.
  • 1:47 - 1:48
    I wanted to treat this problem as if
  • 1:48 - 1:50
    it was a real production problem,
  • 1:50 - 1:52
    and that my only source of information
  • 1:52 - 1:54
    was the test and the code.
  • 1:54 - 1:56
    And so I looked at his.
  • 1:56 - 1:58
    I checked it out of his [repo],
  • 1:58 - 1:59
    and I looked at the problem,
  • 1:59 - 2:02
    and I was so interested in it
    that it became the skeleton
  • 2:02 - 2:07
    around which I have hung
    the ideas for today's talk.
  • 2:07 - 2:10
    I have altered his code just a little bit,
  • 2:10 - 2:11
    but it's just to make
    it easier to talk about.
  • 2:11 - 2:14
    This really is the Gilded Rose Kata,
  • 2:14 - 2:15
    and here's how it goes.
  • 2:15 - 2:17
    There's a Gilded Rose class,
    and it's structured like this.
  • 2:17 - 2:20
    It has attributes for name,
    quality, and days remaining,
  • 2:20 - 2:22
    it sets those in an initializer,
  • 2:22 - 2:25
    and then there's a tick method.
  • 2:25 - 2:28
    Now here's the tick
    method, well actually, no,
  • 2:28 - 2:33
    that's just the first half
    of it, here's the rest.
  • 2:33 - 2:34
    Now, I know you can't read this.
  • 2:34 - 2:36
    Well, don't even try,
    even if you can, alright?
  • 2:36 - 2:37
    This is the whole method.
  • 2:37 - 2:39
    I just want you to get some sense
  • 2:39 - 2:40
    of the size and shape of it.
  • 2:40 - 2:43
    It's a 43-line "if" statement.
  • 2:43 - 2:45
    And this seems really, really hard to me,
  • 2:45 - 2:49
    but I am known to be Bullion-impaired.
  • 2:49 - 2:50
    (laughter)
  • 2:50 - 2:52
    So, I know that my subjective
    sense of how difficult this is
  • 2:52 - 2:55
    to understand is probably not correct,
  • 2:55 - 2:57
    and so instead I used some metrics.
  • 2:57 - 3:01
    I ran a complexity metric
    called "Flog" against it.
  • 3:01 - 3:02
    So Flog is a metric. OK, what's a metric?
  • 3:02 - 3:06
    A metric is a crowdsource
    idea about something.
  • 3:06 - 3:10
    Right? I have my own opinion
    about how complex this is,
  • 3:10 - 3:13
    but I can use this sort of
    wisdom-of-the-crowd metric,
  • 3:13 - 3:15
    the Flog metric, which scores...
  • 3:15 - 3:17
    It's an ABC metric, so
    it scores assignments,
  • 3:17 - 3:19
    branches, and conditionals.
  • 3:19 - 3:21
    It just counts things, and adds them up.
  • 3:21 - 3:22
    Higher scores are worse.
  • 3:22 - 3:24
    They indicate a more complex code,
  • 3:24 - 3:25
    a code that's going to be harder
  • 3:25 - 3:27
    to understand and reason about.
  • 3:27 - 3:30
    And so, Flog says the Gilded
    Rose class scored a 50,
  • 3:30 - 3:34
    and that one method tick scored a 45.
  • 3:34 - 3:36
    (moans)
    Yeah, just hurts, doesn't it?
  • 3:36 - 3:38
    So, Flog says it's complicated,
  • 3:38 - 3:40
    but before we go on I want to introduce
  • 3:40 - 3:44
    a very subjective metric about complexity.
  • 3:44 - 3:46
    So, I spend a lot of time these days
  • 3:46 - 3:51
    going to places and looking
    at code I know nothing about.
  • 3:51 - 3:53
    People call me up, and I go to their shop,
  • 3:53 - 3:54
    and I spend a few days.
  • 3:54 - 3:56
    And as you might imagine,
  • 3:56 - 4:00
    no one calls me if things are going well.
  • 4:00 - 4:02
    (laughter)
    Alright?
  • 4:02 - 4:04
    And when I get there, they don't ask me
  • 4:04 - 4:07
    to look at the code they're proud of.
  • 4:07 - 4:09
    They ask me to look at the most
    heinous bits of their apps,
  • 4:09 - 4:11
    the things that have sort of complex,
  • 4:11 - 4:14
    lengthy contexts in history.
  • 4:14 - 4:17
    Code that has just absolutely
    gotten out of hand.
  • 4:17 - 4:20
    And not only are the
    explanations long and confusing
  • 4:20 - 4:22
    because the problem is hard,
  • 4:22 - 4:22
    but they do that thing that we all do,
  • 4:22 - 4:25
    you know that thing
    you do when you have to
  • 4:25 - 4:26
    explain a bit of code that you wrote
  • 4:26 - 4:28
    that you're embarrassed about to someone?
  • 4:28 - 4:30
    You don't just tell them how it works.
  • 4:30 - 4:32
    You feel compelled to explain
  • 4:32 - 4:35
    all the reasons why it got
    that way. (laughter) Right?
  • 4:35 - 4:37
    You laugh. I do it. I know
    you do it, too, right?
  • 4:37 - 4:39
    It just hurts. We hate that.
  • 4:39 - 4:40
    And so, these explanations are long
  • 4:40 - 4:41
    and confusing and they have lots of
  • 4:41 - 4:44
    sort of sideways kind of information.
  • 4:44 - 4:46
    And there's a point in
    time, I really mean well,
  • 4:46 - 4:47
    but there's a point in time during every
  • 4:47 - 4:50
    explanation when I start
    feeling like that dog,
  • 4:50 - 4:55
    Ginger, in this Gary Larson's cartoon
  • 4:55 - 4:56
    where it starts turning
    into, blah, blah, blah,
  • 4:56 - 4:58
    Sandi, blah, blah, blah. (laughs)
    (laughter)
  • 4:58 - 5:00
    And then suddenly I get startled back
  • 5:00 - 5:02
    into awareness when I hear them say,
  • 5:02 - 5:04
    "So, what do you think we should do
  • 5:04 - 5:07
    "about this line of code?" (laughs)
    (laughter)
  • 5:07 - 5:09
    And it used to terrify me, right?
  • 5:09 - 5:11
    I felt like I had to understand everything
  • 5:11 - 5:13
    in order to help with anything.
  • 5:13 - 5:14
    But it turned out that after a few trips,
  • 5:14 - 5:16
    I realized that there was a really
  • 5:16 - 5:17
    simple thing I could do to help me
  • 5:17 - 5:20
    identify code that they
    could benefit from changing.
  • 5:20 - 5:24
    And I call this the "squint test".
    (laughter)
  • 5:24 - 5:26
    Here's how it works.
  • 5:26 - 5:29
    You squint your eyes, you lean back,
  • 5:29 - 5:31
    and you look at the code.
  • 5:31 - 5:36
    And we're looking for changes in shape,
    (laughter)
  • 5:36 - 5:40
    and changes in color.
  • 5:40 - 5:41
    Changes in shape mean you
    have nested conditionals
  • 5:41 - 5:44
    and they are always going
    to be hard to reason about.
  • 5:44 - 5:46
    Changes in color mean that your code is
  • 5:46 - 5:49
    at differing levels of abstraction,
  • 5:49 - 5:50
    and it means the story it tells
  • 5:50 - 5:52
    is going to be hard to follow.
  • 5:52 - 5:54
    Now, what is it about this code?
  • 5:54 - 5:56
    Well, it has 16 if statements,
  • 5:56 - 5:57
    some of those are not equal to them,
  • 5:57 - 5:59
    and connect something with an "&",
  • 5:59 - 6:01
    there are three magic strings,
    they're used all over,
  • 6:01 - 6:02
    and a number of magic numbers,
  • 6:02 - 6:05
    I don't even know how many.
    (laughter)
  • 6:05 - 6:06
    Now, at least it has tests.
  • 6:06 - 6:08
    Oh, I'm sorry, here are the magic strings.
  • 6:08 - 6:09
    These three things:
  • 6:09 - 6:12
    Brie, Sulfuras, and Backstage passes,
  • 6:12 - 6:13
    whatever that means.
  • 6:13 - 6:18
    And it does have tests, and they pass.
  • 6:18 - 6:22
    Now, there are six skipped tests, alright?
    (laughter)
  • 6:22 - 6:25
    So, I don't know what that's about.
  • 6:25 - 6:27
    And so, I pry open the code, I
    just look at this first test.
  • 6:27 - 6:31
    Oh, sorry, the tests cluster
    around the magic strings,
  • 6:31 - 6:33
    except for this set, which is for
  • 6:33 - 6:34
    something called "Normal",
  • 6:34 - 6:38
    which is never mentioned in
    the "if" statement. (laughter)
  • 6:38 - 6:40
    I suspect there's something
    in an [L's] branch
  • 6:40 - 6:41
    somewhere that matters here, alright?
  • 6:41 - 6:43
    So, I pry open the test and I look at it.
  • 6:43 - 6:46
    Here's one, they all look just like this.
  • 6:46 - 6:50
    I'm selling something, ok?
  • 6:50 - 6:52
    Given a Gilded Rose that has this name,
  • 6:52 - 6:55
    attribute, and quality, those
    are our three [add-a-readers],
  • 6:55 - 7:00
    When I tick, in this case,
    quality goes down by one,
  • 7:00 - 7:02
    days remaining goes down by one,
  • 7:02 - 7:03
    they both go down by one.
  • 7:03 - 7:04
    So, it's as if I'm selling milk,
  • 7:04 - 7:07
    or eggs, or cheese or something
    that has a sell-by date,
  • 7:07 - 7:10
    that's going to expire, where
    they go bad at some date.
  • 7:10 - 7:13
    OK, so, I'm still exploring around,
  • 7:13 - 7:15
    I don't even know what my job is yet,
  • 7:15 - 7:18
    and I look at the six skipped tests,
  • 7:18 - 7:23
    and there is something called "Conjured",
    (laughter)
  • 7:23 - 7:23
    and they all follow the same pattern,
  • 7:23 - 7:25
    all of the tests look like given that
  • 7:25 - 7:27
    when I tick, I see this change.
  • 7:27 - 7:30
    And at this point, I realize, holy crap,
  • 7:30 - 7:33
    I'm supposed to change this code.
    (laughter)
  • 7:33 - 7:37
    And so I tried, I tried,
  • 7:37 - 7:42
    very obediently, I tried, but
    I was a miserable failure.
  • 7:42 - 7:43
    I couldn't do it.
  • 7:43 - 7:45
    That 43 lines of statement defeated me.
  • 7:45 - 7:48
    Every time I went, I would
    like pry open a Conjured test
  • 7:48 - 7:49
    and I'd go make some change
    in that "if" statement
  • 7:49 - 7:52
    to make that test pass, it
    would break something else.
  • 7:52 - 7:53
    I spent hours on it.
  • 7:53 - 7:55
    Now, I am impaired, but
    really, it was hard.
  • 7:55 - 7:57
    It would be hard for you, too, I think.
  • 7:57 - 7:59
    And so, if changing that "if" statement
  • 7:59 - 8:00
    was so hard, you have to ask,
  • 8:00 - 8:02
    why was I trying? Why did I try to do,
  • 8:02 - 8:03
    what possessed me to try to alter
  • 8:03 - 8:09
    that incredibly complicated bit of code?
  • 8:09 - 8:12
    And the answer is, I felt
    like I was supposed to.
  • 8:12 - 8:14
    And here's what happens, right?
  • 8:14 - 8:17
    You write some code,
    someone asks for a change.
  • 8:17 - 8:18
    What do we do?
  • 8:18 - 8:18
    You go look around at the code base
  • 8:18 - 8:21
    for a code that's the closest thing
  • 8:21 - 8:22
    to the new thing you're trying to do,
  • 8:22 - 8:25
    and you put the new code there.
  • 8:25 - 8:25
    That's how we behave.
  • 8:25 - 8:28
    Novices especially, they're
    afraid to make new objects,
  • 8:28 - 8:31
    so they just go put more
    code in where they can
  • 8:31 - 8:33
    find a thing like the thing
    they're trying to add,
  • 8:33 - 8:34
    and if that place already
    has an "if" statement,
  • 8:34 - 8:37
    they just put another branch on it, right?
  • 8:37 - 8:39
    That's how it works.
  • 8:39 - 8:40
    And what happens is,
    so the natural tendency
  • 8:40 - 8:41
    of code is to grow bigger,
  • 8:41 - 8:42
    and bigger, and bigger.
  • 8:42 - 8:43
    And there comes a point, right?
  • 8:43 - 8:44
    It gets bigger, and bigger, and bigger.
  • 8:44 - 8:46
    And there comes a point where it tips,
  • 8:46 - 8:48
    and at that point it's so big that
  • 8:48 - 8:51
    you cannot imagine putting
    code anywhere else.
  • 8:51 - 8:53
    We have a bargain to follow the pattern,
  • 8:53 - 8:56
    and if the pattern is a
    good one, code gets better.
  • 8:56 - 8:59
    And if the pattern is a bad
    one, we exacerbate the problem.
  • 8:59 - 9:02
    Nobody adds a 10-line helper class to a
  • 9:02 - 9:04
    5000-line active record object.
  • 9:04 - 9:06
    They just get bigger.
  • 9:06 - 9:09
    Once they reach a certain
    size, they just get bigger.
  • 9:09 - 9:13
    And so, I could not follow the pattern.
  • 9:13 - 9:14
    I was not good enough
    to follow the pattern,
  • 9:14 - 9:17
    and so I decided I was
    going to make a new pattern,
  • 9:17 - 9:19
    That I was going to refactor this code.
  • 9:19 - 9:21
    Now, this is real refactoring according
  • 9:21 - 9:23
    to the definition of refactoring,
  • 9:23 - 9:26
    I'm going to refactor this code,
  • 9:26 - 9:27
    I'm going to change its arrangement
  • 9:27 - 9:29
    without altering its behavior.
  • 9:29 - 9:31
    I'm not going to try to add Conjured,
  • 9:31 - 9:32
    I'm going to try to move this code
  • 9:32 - 9:35
    around so that I can add Conjured.
  • 9:35 - 9:37
    And for refactoring, for refactoring
  • 9:37 - 9:39
    it's like this test with
    a wall at your back.
  • 9:39 - 9:40
    You've got to have tests,
  • 9:40 - 9:42
    or you don't know what you're doing.
  • 9:42 - 9:43
    And so, I'm just going
    to start at the top.
  • 9:43 - 9:45
    I'm going to start with
    these Normal tests,
  • 9:45 - 9:46
    and I've got this code.
  • 9:46 - 9:47
    This is what tick looks like.
  • 9:47 - 9:49
    Now this is a big, long procedure.
  • 9:49 - 9:51
    This is not object-oriented code.
  • 9:51 - 9:55
    In object-oriented code, you
    have lots of little objects,
  • 9:55 - 9:56
    and you send messages between them.
  • 9:56 - 9:58
    And those messages give you a level of
  • 9:58 - 10:00
    indirection so that you can substitute
  • 10:00 - 10:02
    different objects at the back.
  • 10:02 - 10:05
    Messages create seams so that
    you can do a different thing,
  • 10:05 - 10:09
    and there is no seam here
    because this is a procedure.
  • 10:09 - 10:10
    And so the first thing I have to do
  • 10:10 - 10:13
    if I want to refactor is
    I have to make a seam,
  • 10:13 - 10:15
    and I'm going to do that just by
  • 10:15 - 10:18
    tracking Normal and Bailing.
  • 10:18 - 10:23
    At this point, four tests should
    fail, and they do, alright?
  • 10:23 - 10:27
    And I am not about to add
    more code to the tick method,
  • 10:27 - 10:30
    so I'm just going to
    send a message to myself,
  • 10:30 - 10:33
    and four tests should
    still fail, and they do.
  • 10:33 - 10:34
    And so, now that I believe that I have
  • 10:34 - 10:37
    caught that execution path,
  • 10:37 - 10:38
    I'm going to just break
    open the first test
  • 10:38 - 10:42
    and I'm going to write
    the code and make it pass.
  • 10:42 - 10:45
    Quality goes down by
    one, that's easy enough.
  • 10:45 - 10:48
    I can write that code.
  • 10:48 - 10:52
    Days remaining goes down by
    one, and that test passes.
  • 10:52 - 10:55
    Alright? One down, three to go.
  • 10:55 - 10:56
    Here's the next test.
  • 10:56 - 10:59
    In this case, it looks
    like I'm out of time,
  • 10:59 - 11:03
    I'm on the sale-by-date, so
    now quality goes down by two.
  • 11:03 - 11:07
    So, I'll just make sure my
    old test keeps on passing,
  • 11:07 - 11:11
    and I'll write code to
    make this test pass.
  • 11:11 - 11:13
    And so, now I think two tests should pass,
  • 11:13 - 11:15
    so I should have two failures.
  • 11:15 - 11:18
    But something I just did made some test
  • 11:18 - 11:20
    I haven't looked at
    pass, and we love that.
  • 11:20 - 11:25
    I'm not even going to look
    at it. (laughter) Alright?
  • 11:25 - 11:27
    I don't need to understand
    it, I've got tests. (laughter)
  • 11:27 - 11:29
    OK, so I'm going to just go,
  • 11:29 - 11:30
    I'm going to make this one pass, alright?
  • 11:30 - 11:33
    I'll just open the last one.
  • 11:33 - 11:38
    So, this one says if the
    quality is already zero,
  • 11:38 - 11:40
    don't change it.
  • 11:40 - 11:42
    And so I'm just going
    to wrap this whole thing
  • 11:42 - 11:46
    in an "if" statement, and not
    do anything if quality is zero.
  • 11:46 - 11:49
    OK, so now I'm back to green.
  • 11:49 - 11:52
    That code was not smart or clever,
  • 11:52 - 11:54
    but that's the whole point.
  • 11:54 - 11:56
    Once I get to green, I can now refactor.
  • 11:56 - 11:59
    So my goal is to get to
    green as quickly as possible.
  • 11:59 - 12:03
    Red is not when you
    ponder the abstraction.
  • 12:03 - 12:06
    Red is when you scramble toward green.
  • 12:06 - 12:06
    You're trying to reach to look for the
  • 12:06 - 12:08
    lowest hanging green here,
  • 12:08 - 12:11
    and so I got there, I'm at green now,
  • 12:11 - 12:14
    and I confessed to you already
    that I am Bullion-impaired,
  • 12:14 - 12:14
    and I have written code that even
  • 12:14 - 12:18
    I at this moment do not understand,
  • 12:18 - 12:21
    but now I'm green, so I can refactor.
  • 12:21 - 12:22
    It looks to me that they always
  • 12:22 - 12:25
    subtract one from days remaining,
  • 12:25 - 12:26
    so I'm going to do that first.
  • 12:26 - 12:29
    I like that story better.
  • 12:29 - 12:31
    It looks to me like
    they don't do anything,
  • 12:31 - 12:33
    I can just bail if quality is zero,
  • 12:33 - 12:34
    so I can take that whole outer
  • 12:34 - 12:36
    nesting out of that "if" statement,
  • 12:36 - 12:37
    and now once I get to here,
  • 12:37 - 12:40
    I can ponder these two remaining cases.
  • 12:40 - 12:42
    Are there two cases here?
  • 12:42 - 12:44
    Is there a case where I
    subtract one from quality
  • 12:44 - 12:47
    and two from quality, or
    is this, I don't think so.
  • 12:47 - 12:48
    Now that I look at it this way,
  • 12:48 - 12:51
    I think I always subtract
    one from quality,
  • 12:51 - 12:53
    and there's a special case in which
  • 12:53 - 12:57
    I subtract another, if
    I'm past the sell-by date.
  • 12:57 - 12:58
    And so, I can just delete all that,
  • 12:58 - 13:01
    and now I have this, which is all the same
  • 13:01 - 13:03
    level of abstraction,
    and I can understand it.
  • 13:03 - 13:04
    I love the story this code tells.
  • 13:04 - 13:06
    It's very simple.
  • 13:06 - 13:09
    It was easy to get here,
    and now my test will pass.
  • 13:09 - 13:10
    Alright, so we're going to do this
  • 13:10 - 13:12
    over and over again, much
    more quickly than this one.
  • 13:12 - 13:14
    I'm going to just take you
    through a quick reprise here.
  • 13:14 - 13:17
    So, I create a seam, I
    send a message to myself,
  • 13:17 - 13:19
    I tracked all the
    execution paths into here,
  • 13:19 - 13:21
    I wrote some code, I hated it,
  • 13:21 - 13:22
    I got to green as quick as possible,
  • 13:22 - 13:23
    and then I used green to let me
  • 13:23 - 13:25
    refactor to code that was sensible,
  • 13:25 - 13:27
    and now Normal is done.
  • 13:27 - 13:29
    All the Normal tests passed.
  • 13:29 - 13:30
    So, now I'm just going to bust right
  • 13:30 - 13:32
    through all the other cases.
  • 13:32 - 13:34
    Here's Brie, there's a
    whole bunch of stuff.
  • 13:34 - 13:35
    I'm going to turn that into a case
  • 13:35 - 13:37
    statement so I can track Brie.
  • 13:37 - 13:39
    There are seven tests, and
    they're all failing now,
  • 13:39 - 13:42
    so I have confidence
    that I have caught them.
  • 13:42 - 13:43
    I'm going to write the code,
  • 13:43 - 13:44
    but you don't even need to look at it
  • 13:44 - 13:45
    because you can see how easy it is, right?
  • 13:45 - 13:49
    Now that I am only
    having to write code for
  • 13:49 - 13:50
    one test at a time, it's pretty
    simple to write the code.
  • 13:50 - 13:53
    What I end up with looks like this.
  • 13:53 - 13:56
    And now Brie is done.
  • 13:56 - 14:00
    And now a really
    interesting thing happens.
  • 14:00 - 14:01
    When this stuff was buried in the
  • 14:01 - 14:02
    43-line "if" statement,
  • 14:02 - 14:07
    I had no idea the ways in which
    Normal and Brie were like,
  • 14:07 - 14:10
    but now that I'm using this,
    they seem a lot alike to me.
  • 14:10 - 14:13
    Now, there's differences sort
    of in the driving data here,
  • 14:13 - 14:15
    but the algorithm, you can see the shape
  • 14:15 - 14:17
    of the algorithm here, and the
    algorithm is really the same.
  • 14:17 - 14:19
    And it is very tempting.
  • 14:19 - 14:22
    We've had the DRY rule
    browbeat into us so strongly,
  • 14:22 - 14:24
    it's very tempting at this point.
  • 14:24 - 14:26
    I'm on a road, I'm on a refactoring road.
  • 14:26 - 14:28
    It's very tempting now to go on a tangent
  • 14:28 - 14:30
    and try to clean this up.
  • 14:30 - 14:31
    Because we believe the greatest,
  • 14:31 - 14:34
    we've been taught like the
    greatest virtue is DRY.
  • 14:34 - 14:36
    And I will tell you that's
    the wrong idea here.
  • 14:36 - 14:38
    I'm about to get a lot more information
  • 14:38 - 14:40
    about what this algorithm looks like,
  • 14:40 - 14:43
    and I need to finish
    the refactoring I'm on
  • 14:43 - 14:44
    before I go on any tangents,
    so I'm going to notice
  • 14:44 - 14:48
    that similarity and keep the duplication,
  • 14:48 - 14:51
    and just keep on going down
    this path to see where it leads.
  • 14:51 - 14:53
    And this brings me to my
    first big point of this talk.
  • 14:53 - 14:57
    It is far cheaper to keep duplication
  • 14:57 - 15:00
    than it is to have to mess
    with the wrong abstraction.
  • 15:00 - 15:01
    The first rule we teach novices
  • 15:01 - 15:03
    is don't repeat yourself - DRY.
  • 15:03 - 15:06
    But have you ever thought about
    why we teach them that rule?
  • 15:06 - 15:09
    It's because they can't understand
    anything else. (laughter)
  • 15:09 - 15:11
    They don't know anything, but by God,
  • 15:11 - 15:14
    they can recognize duplication,
    and it's a good rule.
  • 15:14 - 15:15
    I'm not saying it's a bad rule,
  • 15:15 - 15:21
    but I'm saying that now you're
    grown up, you know more,
  • 15:21 - 15:23
    and you have enough experience
  • 15:23 - 15:25
    now to tolerate a little duplication
  • 15:25 - 15:26
    and wait on a better abstraction.
  • 15:26 - 15:30
    It's really hard to deal
    with the wrong abstraction.
  • 15:30 - 15:32
    I often make a "dup tag".
  • 15:32 - 15:34
    You know how you can make a "to do" tag?
  • 15:34 - 15:35
    Like people say, "Oh, I'm going to
  • 15:35 - 15:37
    "lose track of my duplication."
  • 15:37 - 15:38
    Well, fix that problem.
  • 15:38 - 15:40
    Make a "dup tag" and give
    every "dup" a number,
  • 15:40 - 15:43
    so if you have the same
    code in two or three places,
  • 15:43 - 15:46
    like if that's the sixth
    instance of duplication,
  • 15:46 - 15:47
    give it an ID like a database,
  • 15:47 - 15:49
    and put "dup six" in a bunch
    of places in your code.
  • 15:49 - 15:50
    You'll know.
  • 15:50 - 15:52
    You'll see the duplication
    if you change a part,
  • 15:52 - 15:54
    like fix the problem of
    not being able to find it,
  • 15:54 - 15:57
    rather than reaching too
    soon for an abstraction.
  • 15:57 - 16:00
    It's much easier to
    deal with a duplication.
  • 16:00 - 16:01
    Alright, so moving on.
  • 16:01 - 16:03
    Here's Sulfuras. There's three tests.
  • 16:03 - 16:06
    You would think if I put my
    shim in and put an empty method,
  • 16:06 - 16:12
    I would have three test failures.
    (laughter)
  • 16:12 - 16:14
    And yet they all pass.
  • 16:14 - 16:15
    So, what's this about?
  • 16:15 - 16:17
    Well, I look at the test and I realize
  • 16:17 - 16:18
    in all the tests [unintelligible] that
  • 16:18 - 16:21
    nothing happens if it's Sulfuras.
  • 16:21 - 16:22
    And again, I had no idea when I was
  • 16:22 - 16:23
    looking at that 43 lines of statement
  • 16:23 - 16:26
    that somehow it all asserted
    that nothing happened,
  • 16:26 - 16:31
    so it turns out this is the code
    that makes the tests pass. (laughter)
  • 16:31 - 16:34
    How nice is that? We love that, OK?
  • 16:34 - 16:35
    So here's Backstage, and
    there's a whole bunch of these,
  • 16:35 - 16:37
    and it looks like this.
  • 16:37 - 16:39
    That's the code that makes the tests pass.
  • 16:39 - 16:42
    And so now, we're totally back to green.
  • 16:42 - 16:44
    This looks exactly like
    it did when I started.
  • 16:44 - 16:47
    And this is what I got.
  • 16:47 - 16:48
    I put this case statement in the front,
  • 16:48 - 16:51
    and it tracked all the execution paths.
  • 16:51 - 16:53
    I have a bunch of methods
    that look like this
  • 16:53 - 16:56
    that I created and added
    to the Gilded Rose class,
  • 16:56 - 16:57
    and the rest of the tick methods still
  • 16:57 - 17:00
    contains that monstrous
    43-line "if" statement,
  • 17:00 - 17:03
    which I don't understand,
    but I no longer need,
  • 17:03 - 17:07
    so I'm just going to delete it, it's gone.
  • 17:07 - 17:12
    Now if you don't have good tests,
    this may freak you out, (laughter)
  • 17:12 - 17:14
    but then if you feel freaked out by this
  • 17:14 - 17:15
    and you don't have good tests,
  • 17:15 - 17:16
    you're really making a choice here, right?
  • 17:16 - 17:19
    If you have code that you don't understand
  • 17:19 - 17:21
    and that you're afraid to change,
  • 17:21 - 17:25
    you can keep it forever, if
    you think that's a good idea,
  • 17:25 - 17:28
    or you can put some kind of test [harness)
  • 17:28 - 17:29
    around it so that you can refactor,
  • 17:29 - 17:32
    but keeping it forever is
    not really a good choice,
  • 17:32 - 17:32
    so you want to get to the point where
  • 17:32 - 17:35
    you have confidence that
    you can safely refactor,
  • 17:35 - 17:37
    and it means you never have
    to understand that code,
  • 17:37 - 17:40
    you can do characterization
    tests around the edges,
  • 17:40 - 17:41
    so that you'll have green,
  • 17:41 - 17:42
    you'll have a wall at your back for tests,
  • 17:42 - 17:44
    and then you can refactor your way,
  • 17:44 - 17:45
    to the point where you can delete
  • 17:45 - 17:48
    the code that you don't understand.
  • 17:48 - 17:49
    And the moral of this story is that
  • 17:49 - 17:52
    small methods are simple.
  • 17:52 - 17:52
    Here we have it.
  • 17:52 - 17:54
    This is the code we just wrote.
  • 17:54 - 17:55
    This is the squint test
    version, don't try to read it.
  • 17:55 - 17:57
    This is the code we
    just wrote on the right,
  • 17:57 - 17:58
    and this is how we start it on the left.
  • 17:58 - 18:02
    You notice that the shape is flat,
  • 18:02 - 18:04
    and the colors are starting to cluster.
  • 18:04 - 18:07
    Now, again I believe in metrics, because
  • 18:07 - 18:08
    I know that my personal notion of what
  • 18:08 - 18:11
    is simple or complex is just my opinion,
  • 18:11 - 18:15
    and I totally know that
    metrics are fallible,
  • 18:15 - 18:18
    but human opinion is no more precise.
  • 18:18 - 18:21
    And that metrics are kind
    of a crowdsource idea
  • 18:21 - 18:23
    of what a bunch of people
    thought a metric could be.
  • 18:23 - 18:26
    It is a useful data point
    for me to compare to my own.
  • 18:26 - 18:28
    The original class Flogged to 50,
  • 18:28 - 18:31
    and this new class Flogs to 40,
  • 18:31 - 18:33
    but that overstates its complexity
  • 18:33 - 18:33
    because now there's a bunch of methods,
  • 18:33 - 18:37
    and the most complex method is
    Backstage and if Flogs to 12.
  • 18:37 - 18:40
    This code is way simpler.
  • 18:40 - 18:41
    Well, this is great, and you'd think that
  • 18:41 - 18:44
    everyone would just do this.
  • 18:44 - 18:46
    And so, it's an interesting
    question why they don't.
  • 18:46 - 18:47
    Now, one of the things I already told you,
  • 18:47 - 18:49
    I already gave you one reason, right?
  • 18:49 - 18:51
    We do more of what's there.
  • 18:51 - 18:53
    And so, the tendency is to add more
  • 18:53 - 18:54
    to the "if" statement, if that's there,
  • 18:54 - 18:56
    but I think there's another reason
  • 18:56 - 18:59
    why we don't undertake these refactorings,
  • 18:59 - 19:00
    and it's because of this.
  • 19:00 - 19:03
    I'm just going to make the
    50 smaller and move it over.
  • 19:03 - 19:06
    It took me 10 refactoring steps to get
  • 19:06 - 19:10
    from the big conditional to
    a bunch of small methods,
  • 19:10 - 19:15
    and here's the Flog score of
    all the intermediate steps.
  • 19:15 - 19:17
    All the intermediate refactorings
  • 19:17 - 19:20
    made code more complicated.
  • 19:20 - 19:22
    I know that I'm going to get to that 40.
  • 19:22 - 19:24
    I understand the principles
    of object-oriented design,
  • 19:24 - 19:26
    and I know the value of small methods,
  • 19:26 - 19:29
    and because of that, I
    believe in the refactorings,
  • 19:29 - 19:32
    and that lets me tolerate
    the intermediate complexity.
  • 19:32 - 19:35
    But if you don't know,
    if you haven't learned
  • 19:35 - 19:36
    about the value of small methods,
  • 19:36 - 19:39
    it's hard to undertake
    those intermediate steps.
  • 19:39 - 19:44
    They seem like academic things that
  • 19:44 - 19:49
    people will do that are
    for some pie-in-the-sky
  • 19:49 - 19:51
    principle that don't improve code,
  • 19:51 - 19:53
    but I can promise you that if you can see
  • 19:53 - 19:55
    far enough to see to the end,
  • 19:55 - 20:00
    this intermediate complexity
    leads to ultimate simplicity.
  • 20:00 - 20:03
    And, so now I'm going to
    circle back around my task,
  • 20:03 - 20:04
    now that I've done this refactoring,
  • 20:04 - 20:06
    I can circle back around my original task
  • 20:06 - 20:08
    which is to implement Conjure.
  • 20:08 - 20:10
    How should I do this?
  • 20:10 - 20:14
    Here's what I got, I've got this.
  • 20:14 - 20:18
    Should I do that?
  • 20:18 - 20:21
    It would be easy, it would be really easy.
  • 20:21 - 20:24
    The answer to that is
    "no", I should not do that.
  • 20:24 - 20:27
    That is not the way I
    should solve this problem,
  • 20:27 - 20:30
    and it's because this
    code is not open/closed.
  • 20:30 - 20:34
    It is not open for extension,
    and closed for modification.
  • 20:34 - 20:38
    Open/closed supplies the
    "O" in [solid], and it is,
  • 20:38 - 20:39
    and I'm going to say it right out loud,
  • 20:39 - 20:43
    it's a principle of
    object-oriented design.
  • 20:43 - 20:46
    It's one of the pieces
    of cumulative wisdom
  • 20:46 - 20:48
    created by folks who've written
  • 20:48 - 20:49
    a mountain of object-oriented code,
  • 20:49 - 20:52
    and they have experienced every
  • 20:52 - 20:55
    possible kind of programming pain.
  • 20:55 - 20:58
    And over time, they have
    noticed some principles,
  • 20:58 - 21:01
    and they developed a style guide
  • 21:01 - 21:04
    about how to organize code.
  • 21:04 - 21:05
    That's what object-oriented design is.
  • 21:05 - 21:07
    That's what the rules of
    object-oriented design are.
  • 21:07 - 21:11
    It's a style guide about
    how to organize code
  • 21:11 - 21:13
    with all the obvious tradeoffs,
  • 21:13 - 21:15
    all the places where you
    can make your own decisions.
  • 21:15 - 21:19
    In this case, you can feel free
    to ignore their discoveries,
  • 21:19 - 21:20
    in which case you'll get to experience
  • 21:20 - 21:23
    all that pain over again for yourself.
  • 21:23 - 21:25
    That's what will happen.
  • 21:25 - 21:27
    On the macro level, this style guide says
  • 21:27 - 21:31
    it's best to arrange code
    so that adding new behavior
  • 21:31 - 21:34
    does not require that
    you edit existing code.
  • 21:34 - 21:36
    I know that seems impossible.
  • 21:36 - 21:37
    I'm going to say it again, right?
  • 21:37 - 21:40
    Open/closed says you ought to
    be able to add new behavior
  • 21:40 - 21:43
    without editing existing code.
  • 21:43 - 21:45
    Now, forget about how impossible
    that seems for a minute.
  • 21:45 - 21:47
    I just want you to
    imagine something for me.
  • 21:47 - 21:53
    Imagine the world, imagine
    your apps, if that is true.
  • 21:53 - 21:56
    Imagine that you can add new behavior
  • 21:56 - 21:58
    without editing existing code.
  • 21:58 - 22:01
    Think about what that means.
  • 22:01 - 22:02
    It means you always have green tests,
  • 22:02 - 22:04
    it means you are always safe,
  • 22:04 - 22:07
    it means you never cause some
  • 22:07 - 22:09
    distant and unrelated side-effect.
  • 22:09 - 22:11
    That is a sweet, sweet world,
  • 22:11 - 22:13
    if your code is open/closed.
  • 22:13 - 22:15
    And so, on the macro
    level, we are trying to get
  • 22:15 - 22:16
    to the point where we
    can add new behavior,
  • 22:16 - 22:18
    without editing existing code.
  • 22:18 - 22:21
    And on the micro level,
    what that means here,
  • 22:21 - 22:23
    right now, in this code,
  • 22:23 - 22:25
    is that when we see methods that have
  • 22:25 - 22:28
    a repeating prefix or repeating suffix,
  • 22:28 - 22:30
    there is a tortured object in there
  • 22:30 - 22:35
    that's trying to get out.
    (laughter)
  • 22:35 - 22:37
    Right here, in this place,
  • 22:37 - 22:38
    you're about to make a decision
  • 22:38 - 22:40
    that's going to have consequences
  • 22:40 - 22:43
    that echo through your code base forever.
  • 22:43 - 22:46
    Are you going to write procedures,
  • 22:46 - 22:48
    or are you going to trust objects?
  • 22:48 - 22:51
    If you insist on having
    all the logic visible,
  • 22:51 - 22:53
    right here where you can see it,
  • 22:53 - 22:54
    you are insisting really on knowing
  • 22:54 - 22:57
    both the condition on which you switch,
  • 22:57 - 22:58
    and the thing that you do,
  • 22:58 - 23:00
    the action that you take
    when that switch happens.
  • 23:00 - 23:02
    If you're uncomfortable, and unless
  • 23:02 - 23:06
    you know both those things
    at once in this file,
  • 23:06 - 23:07
    under your eyes, in this
    code, then you're going to
  • 23:07 - 23:10
    be forced to add a new method right here.
  • 23:10 - 23:13
    You have to put that conjured
    tick method right here.
  • 23:13 - 23:15
    But if you don't, if you're OK with that,
  • 23:15 - 23:16
    you can listen to object-oriented design.
  • 23:16 - 23:19
    It says that when you
    have differing prefixes,
  • 23:19 - 23:21
    and common suffixes,
    then what you really have
  • 23:21 - 23:25
    is a normal class that
    ought to have a method tick,
  • 23:25 - 23:27
    and a Gilded Rose ought to be holding on
  • 23:27 - 23:30
    to an instance of it.
  • 23:30 - 23:32
    And it is real easy to right that code.
  • 23:32 - 23:35
    If you can think of that
    thing, thinking of the thing
  • 23:35 - 23:37
    is far harder than writing the code.
  • 23:37 - 23:38
    Here's how the code looks.
  • 23:38 - 23:40
    I've got this Normal tick method,
  • 23:40 - 23:42
    I'm just going to call it "tick",
  • 23:42 - 23:43
    I'm going to put in a Normal class,
  • 23:43 - 23:45
    I'm going to throw a cruft in there to get
  • 23:45 - 23:48
    the initialization and
    the attributes defined,
  • 23:48 - 23:49
    I'm going to go back into Gilded Rose
  • 23:49 - 23:51
    and the Normal tick method there,
  • 23:51 - 23:53
    I'll get an instance of my new class,
  • 23:53 - 23:57
    and I'll forward this
    message - boom, that's it.
  • 23:57 - 23:59
    Alright, well, so I've got this,
  • 23:59 - 24:02
    so Normal is an object,
    but nothing else is,
  • 24:02 - 24:04
    and I'm about to go back on the path where
  • 24:04 - 24:07
    I have to increase
    intermediate complexity,
  • 24:07 - 24:09
    because look what just happened, alright?
  • 24:09 - 24:11
    My new Normal tick method looks like this.
  • 24:11 - 24:12
    It uses this item class.
  • 24:12 - 24:14
    But Brie, the Brie tick method is still
  • 24:14 - 24:17
    calculated inside the Gilded Rose.
  • 24:17 - 24:18
    The quality and days remaining are part
  • 24:18 - 24:20
    of the public API for Gilded Rose,
  • 24:20 - 24:21
    and so now I have to say, well,
  • 24:21 - 24:23
    if I have an item, go
    get the item's quality,
  • 24:23 - 24:25
    otherwise, get the one I know about.
  • 24:25 - 24:29
    And I have to do the same thing
    for days remaining, alright?
  • 24:29 - 24:33
    It looks messy, but it's
    short-term, it will go away.
  • 24:33 - 24:35
    And so, let's just walk
    through all the other objects.
  • 24:35 - 24:36
    Now that you understand this pattern,
  • 24:36 - 24:37
    it's really easy, right?
  • 24:37 - 24:39
    Class Brie, move method tick,
  • 24:39 - 24:42
    put the cruft in there,
    forward the message.
  • 24:42 - 24:43
    Easy enough.
  • 24:43 - 24:45
    This is really interesting.
  • 24:45 - 24:48
    Now some trust is coming into play, right?
  • 24:48 - 24:49
    I have an empty method,
    look what I have to do.
  • 24:49 - 24:51
    Make a new class, put the method in,
  • 24:51 - 24:52
    pry this method open, get an instance
  • 24:52 - 24:54
    of that class, forward the message.
  • 24:54 - 24:58
    You can be forgiven for
    being suspicious about this,
  • 24:58 - 25:00
    but if you trust the refactorings,
  • 25:00 - 25:01
    you have confidence that this is
  • 25:01 - 25:03
    going to turn out well in the end.
  • 25:03 - 25:06
    I'm not going to diverge,
    I'm not taking a detour,
  • 25:06 - 25:07
    I'm going to go all the way down
  • 25:07 - 25:09
    this path and finish this refactoring.
  • 25:09 - 25:11
    Backstage, I'll make the tick method,
  • 25:11 - 25:16
    I'll make that stuff,
    I'll do the forwarding.
  • 25:16 - 25:21
    Alright, so now they're all
    objects and I've got this.
  • 25:21 - 25:23
    And I'm back here.
  • 25:23 - 25:27
    So now, in the beginning, I moved logic
  • 25:27 - 25:28
    into methods of their own,
  • 25:28 - 25:30
    because I didn't want to put a bunch
  • 25:30 - 25:31
    of code in this case statement,
  • 25:31 - 25:34
    but now that I have objects,
    everything is simpler,
  • 25:34 - 25:37
    and I'm going to just start
    rewinding my decisions.
  • 25:37 - 25:39
    I'm just going to delete the method,
  • 25:39 - 25:41
    and shove the code that
    used to be in the method
  • 25:41 - 25:45
    back up in the branches
    of the case statement.
  • 25:45 - 25:47
    We'll do that.
  • 25:47 - 25:48
    Now, earlier I said that duplication was
  • 25:48 - 25:50
    cheaper than the wrong abstraction,
  • 25:50 - 25:52
    but now we're starting
    to see abstractions,
  • 25:52 - 25:55
    and I'm just going to go and
    abstract away some duplication.
  • 25:55 - 25:57
    I'm going to put the cruft back up in here
  • 25:57 - 26:00
    so in Gilded Rose, I no longer need that.
  • 26:00 - 26:02
    What I really need to do
    is be able to get an item.
  • 26:02 - 26:05
    And the way I need to get an item is that.
  • 26:05 - 26:06
    That's how I'm going to get it.
  • 26:06 - 26:07
    If I just knew the class name,
  • 26:07 - 26:10
    I could send that message
    to it, and it would work.
  • 26:10 - 26:12
    And it's actually really easy
    to figure out the class name.
  • 26:12 - 26:15
    The code's already here.
  • 26:15 - 26:17
    It's this, there it is.
  • 26:17 - 26:19
    That will get me the class name back,
  • 26:19 - 26:21
    so if I just give that a name,
  • 26:21 - 26:23
    I can send that message to myself,
  • 26:23 - 26:26
    and now I have the right kind of item.
  • 26:26 - 26:29
    I don't need a name anymore,
    so that got simpler.
  • 26:29 - 26:31
    So now, I have separated
    the reason I'm switching
  • 26:31 - 26:35
    from the thing I do when I switch.
  • 26:35 - 26:36
    And I can just forget about what's
  • 26:36 - 26:38
    inside that class [form] method.
  • 26:38 - 26:39
    I don't really care anymore.
  • 26:39 - 26:40
    It just answers the right class.
  • 26:40 - 26:42
    It's going to work.
  • 26:42 - 26:43
    It's going to hand back a thing that can
  • 26:43 - 26:45
    answer the message I'm
    going to send to it.
  • 26:45 - 26:47
    And now tick looks like
    that and these now,
  • 26:47 - 26:48
    so I'm rewinding the complexity,
  • 26:48 - 26:49
    I don't need this anymore.
  • 26:49 - 26:53
    I have items in every case,
    so I'll get rid of all that.
  • 26:53 - 26:56
    And now here's the whole
    body of code that I have.
  • 26:56 - 26:58
    I'm holding an instance of
    the correct item object,
  • 26:58 - 27:01
    and I just sent it the tick method.
  • 27:01 - 27:02
    so, we have four different,
    down at the bottom there,
  • 27:02 - 27:05
    you can see we have four
    different kinds of item classes,
  • 27:05 - 27:09
    but from Gilded Rose's point
    of view, item is a role.
  • 27:09 - 27:13
    It doesn't think of it like
    this, it thinks of it like that.
  • 27:13 - 27:16
    You just need someone in there
    that can answer that API,
  • 27:16 - 27:19
    that knows those messages, it's
    a [duck] type, if you will.
  • 27:19 - 27:20
    And if you look at the code I have now,
  • 27:20 - 27:23
    the message passing works like this.
  • 27:23 - 27:24
    All these messages get forwarded,
  • 27:24 - 27:27
    and if you had a Foo
    that had a Gilded Rose
  • 27:27 - 27:29
    that sent those messages,
    it would look like this.
  • 27:29 - 27:32
    And so, now I'm in a situation like this.
  • 27:32 - 27:34
    When an object's only purpose is to
  • 27:34 - 27:36
    forward messages somewhere else,
  • 27:36 - 27:39
    you have to wonder if it
    justifies its existence.
  • 27:39 - 27:41
    This actually is a code
    [unintelligible] that has a name,
  • 27:41 - 27:43
    and its name is Middleman.
  • 27:43 - 27:46
    So, if that's all the Gilded Rose does,
  • 27:46 - 27:48
    it probably shouldn't
    exist, but it turns out
  • 27:48 - 27:51
    it still does something important.
  • 27:51 - 27:56
    Given a string like Normal,
    it can figure out what
  • 27:56 - 27:59
    item class, what class plays
    the appropriate item role.
  • 27:59 - 28:02
    And so now, I'm going to use another word
  • 28:02 - 28:05
    that you should love, you
    should love this word.
  • 28:05 - 28:06
    Gilded Rose, the only thing that
  • 28:06 - 28:10
    Gilded Rose is is an item factory.
  • 28:10 - 28:12
    I just need to figure out
    how to get the right object,
  • 28:12 - 28:13
    and then I can send it a message.
  • 28:13 - 28:15
    We've simplified our problem by
  • 28:15 - 28:18
    separating the thing I'm switching on
  • 28:18 - 28:20
    from the thing I do when I switch.
  • 28:20 - 28:21
    We've divided those things in half,
  • 28:21 - 28:24
    so I can make the code
    [unintelligible] less,
  • 28:24 - 28:26
    and we can do smaller things.
  • 28:26 - 28:28
    I don't need to know what they do,
  • 28:28 - 28:31
    I just need to know how
    to get the right one.
  • 28:31 - 28:33
    And so, I'm going to change this
    code to reflect the reality.
  • 28:33 - 28:35
    I'm going to make Gilded Rose a module.
  • 28:35 - 28:38
    I'm going to say four,
    some people put new.
  • 28:38 - 28:40
    They make a new method on module,
  • 28:40 - 28:42
    and I just can't bear that,
  • 28:42 - 28:44
    but it's OK with me if you do it that way.
  • 28:44 - 28:45
    So, I have to make it a class method
  • 28:45 - 28:47
    because I'm calling it.
  • 28:47 - 28:48
    I'm no longer keeping
    an instance of anything,
  • 28:48 - 28:50
    so I don't need an [add-a-reader].
  • 28:50 - 28:52
    All these Middleman messages now,
  • 28:52 - 28:53
    since you're really
    going to talk to the item
  • 28:53 - 28:55
    that you get back when you call four,
  • 28:55 - 28:58
    all these messages, they just go away.
  • 28:58 - 29:00
    So now, this is what we have.
  • 29:00 - 29:02
    And the way you use it is you send four
  • 29:02 - 29:04
    to Gilded Rose, and it gives back an item,
  • 29:04 - 29:07
    and it's the item that you talked to.
  • 29:07 - 29:09
    And so, now that we've
    fixed the Gilded Rose,
  • 29:09 - 29:11
    I'm going to turn my attention to the
  • 29:11 - 29:16
    classes that play the item role.
  • 29:16 - 29:19
    There's a lot of duplication here that
  • 29:19 - 29:20
    we've been tolerating for a long time.
  • 29:20 - 29:21
    They all have this in them.
  • 29:21 - 29:24
    And I'm going to create
    an inheritance hierarchy,
  • 29:24 - 29:26
    and clean that up.
  • 29:26 - 29:27
    I'm going to make a little item class,
  • 29:27 - 29:30
    push all that stuff up to
    it, then all these guys,
  • 29:30 - 29:31
    I can delete that code
    from all these guys,
  • 29:31 - 29:35
    and make them subclasses of item.
  • 29:35 - 29:38
    Now, despite what you may have heard,
  • 29:38 - 29:41
    inheritance is not evil,
  • 29:41 - 29:44
    and I can tell you exactly
    when it's safe to use it.
  • 29:44 - 29:45
    Now here's what you want.
  • 29:45 - 29:48
    You want a shallow, narrow hierarchy,
  • 29:48 - 29:50
    you don't want it to be deep,
  • 29:50 - 29:52
    and you don't want it to be wide, alright?
  • 29:52 - 29:54
    Shallow and narrow, you would
    like the subclasses to be,
  • 29:54 - 29:57
    OK, I will say this twice.
  • 29:57 - 29:58
    You would like the subclasses to be
  • 29:58 - 30:02
    at the leaf nodes of
    your object graph, right?
  • 30:02 - 30:03
    So, you have objects, and
    you've got other objects,
  • 30:03 - 30:04
    and you've got other objects,
  • 30:04 - 30:06
    and down at the end of your sort of tree,
  • 30:06 - 30:10
    there are objects that don't
    know about any other things.
  • 30:10 - 30:11
    Right? So we want the subclasses to be
  • 30:11 - 30:14
    the leaf nodes of the object
    we have to be at the edge,
  • 30:14 - 30:17
    and we want all the subclasses to use
  • 30:17 - 30:20
    all the codes in the superclass.
  • 30:20 - 30:21
    Now, I'm going to repeat that again.
  • 30:21 - 30:24
    Shallow, narrow, subclasses
    at the leaf nodes,
  • 30:24 - 30:27
    and subclasses use all the
    behavior in the superclass.
  • 30:27 - 30:29
    If that is the problem that you have,
  • 30:29 - 30:31
    there is no better
    solution than inheritance,
  • 30:31 - 30:35
    and you are free to use it.
  • 30:35 - 30:37
    So, however, although I love inheritance,
  • 30:37 - 30:40
    I use it in appropriate
    ways, and it is not evil,
  • 30:40 - 30:43
    but sometimes we are.
    (laughter)
  • 30:43 - 30:44
    You might be.
  • 30:44 - 30:46
    And it's easy to get inheritance wrong,
  • 30:46 - 30:50
    and this tree has a little problem,
  • 30:50 - 30:52
    and it's this, I don't like this.
  • 30:52 - 30:55
    The public API of item is
    quality of days remaining,
  • 30:55 - 30:57
    and the public API of
    those four subclasses
  • 30:57 - 30:59
    contains one additional method tick.
  • 30:59 - 31:02
    And I think that superclass
    ought to play the item role,
  • 31:02 - 31:04
    which means to me it's
    got to implement tick.
  • 31:04 - 31:07
    And the question then becomes,
  • 31:07 - 31:08
    what is the appropriate implementation
  • 31:08 - 31:11
    of tick to put in the superclass?
  • 31:11 - 31:13
    You could define tick and
    have it raise an error
  • 31:13 - 31:15
    that says subclasses
    have to implement tick.
  • 31:15 - 31:18
    You could do that, I do that sometimes,
  • 31:18 - 31:20
    but here I think there's
    a default implementation
  • 31:20 - 31:24
    that's appropriate, and it's this.
  • 31:24 - 31:26
    Do nothing.
  • 31:26 - 31:29
    It's perfectly OK with
    me, tick to do nothing.
  • 31:29 - 31:31
    And now, I did that because the
  • 31:31 - 31:32
    inheritance heirarchy bothered me,
  • 31:32 - 31:35
    and I'm just removing code now.
  • 31:35 - 31:38
    Now that I've done that,
    you might notice something
  • 31:38 - 31:40
    about Sulfuras' implementation of tick.
  • 31:40 - 31:42
    It overrides item, it subclasses item to
  • 31:42 - 31:46
    override tick to do exactly
    what the superclass does.
  • 31:46 - 31:50
    And what that means is that here
  • 31:50 - 31:55
    it would be equally correct to say this,
  • 31:55 - 31:59
    which means that this
    class is not necessary
  • 31:59 - 32:02
    and all the intermediate
    complexity that I created
  • 32:02 - 32:06
    as I was following this
    refactoring just went away.
  • 32:06 - 32:10
    There is no more Sulfuras class.
  • 32:10 - 32:12
    So, I'm going to do one last thing.
  • 32:12 - 32:12
    We're almost finished here.
  • 32:12 - 32:14
    So, this case statement contains
  • 32:14 - 32:17
    two different types of information.
  • 32:17 - 32:21
    It contains a set of
    string to class mappings,
  • 32:21 - 32:24
    and it contains the
    algorithm to hook them up.
  • 32:24 - 32:26
    And I contend to you that case statements
  • 32:26 - 32:28
    are meant for business logic,
  • 32:28 - 32:30
    and this doesn't really
    feel like business logic.
  • 32:30 - 32:33
    This feels like configuration information.
  • 32:33 - 32:37
    And so, I'm just going to
    extract configuration data here.
  • 32:37 - 32:40
    I'm going to make a hash,
    and then I'm going to change
  • 32:40 - 32:44
    the algorithm to just be the
    algorithm that uses that hash.
  • 32:44 - 32:47
    Now, in real life, this would probably go
  • 32:47 - 32:49
    through some transitions
    where now the hash
  • 32:49 - 32:51
    can change independently of the algorithm
  • 32:51 - 32:53
    that matches these things up,
  • 32:53 - 32:54
    and if you find the hash changing a lot,
  • 32:54 - 32:57
    you might be tempted to
    maybe make it a Yamo file,
  • 32:57 - 32:59
    and if you find the Yamo
    file changing a lot,
  • 32:59 - 33:02
    you might be tempted
    to put in the database.
  • 33:02 - 33:03
    Now I can vary that data independently of
  • 33:03 - 33:07
    this rule about how they
    get hooked up together.
  • 33:07 - 33:10
    And so, that's it, that's
    the whole refactoring.
  • 33:10 - 33:13
    We've got a bunch of small objects
  • 33:13 - 33:14
    now instead of small methods.
  • 33:14 - 33:16
    Here's the whole code.
  • 33:16 - 33:19
    In the Gilded Rose module,
    there's an item class,
  • 33:19 - 33:22
    and then there's three item subclasses,
  • 33:22 - 33:24
    each of which contains a tick method.
  • 33:24 - 33:26
    There's a set of configuration information
  • 33:26 - 33:29
    that's used by this algorithm to decide
  • 33:29 - 33:32
    what item class is
    appropriate for what string.
  • 33:32 - 33:36
    Here's the squint testable
    version of small objects,
  • 33:36 - 33:38
    and this is it compared to
    the original big conditional.
  • 33:38 - 33:41
    Now that's interesting that
    in the small objects string,
  • 33:41 - 33:42
    it looks like it's nested
    too deep, but it's not.
  • 33:42 - 33:44
    I just have the classes
    inside the modules.
  • 33:44 - 33:48
    Right? So, that is only
    really one level of indenting.
  • 33:48 - 33:49
    The more interesting comparison here is
  • 33:49 - 33:52
    the squint test between
    the intermediate solution,
  • 33:52 - 33:54
    the small method solution,
    and the small object solution.
  • 33:54 - 33:57
    Notice that small objects
    is a little bit longer,
  • 33:57 - 34:01
    but the colors are clustered
    more tightly together.
  • 34:01 - 34:02
    So we have really distilled the things
  • 34:02 - 34:06
    that change together in single places.
  • 34:06 - 34:08
    Here's the Flog scores
    that we used to have.
  • 34:08 - 34:11
    So, OK, I have time to make you guess.
  • 34:11 - 34:14
    I made a bunch of small
    objects...what's the Flog score?
  • 34:14 - 34:19
    Male:Well, 15.
  • 34:19 - 34:21
    Sandi:But you know what? OK, here.
  • 34:21 - 34:24
    What's in the intermediate,
    we'll come back to that.
  • 34:24 - 34:25
    I like that 15 guess, that
    was an excellent guess,
  • 34:25 - 34:26
    and you'll know why in a minute.
  • 34:26 - 34:30
    Here's the intermediate complexity scores.
  • 34:30 - 34:31
    Alright, so I've got this.
  • 34:31 - 34:33
    That 33 vastly overstates the complexity
  • 34:33 - 34:37
    of the final solution,
    and it's because of this.
  • 34:37 - 34:39
    When you have the first
    [string] was one class,
  • 34:39 - 34:41
    the whole Gilded Rose class,
  • 34:41 - 34:42
    and you gotta kind of
    know all about that class.
  • 34:42 - 34:44
    And the second solution
    of the small methods
  • 34:44 - 34:46
    it was the Gilded Rose version two, right?
  • 34:46 - 34:49
    It was a single class, and
    you gotta kind of know,
  • 34:49 - 34:50
    you've gotta hold that class in your head.
  • 34:50 - 34:53
    This third solution is a
    bunch of small classes,
  • 34:53 - 34:54
    it's a bunch of different classes,
  • 34:54 - 34:58
    and you don't need to reason
    about all of them at once.
  • 34:58 - 34:59
    As a matter of fact,
    you really only need to
  • 34:59 - 35:01
    read the most complicated object in there,
  • 35:01 - 35:03
    and the most complicated
    class is a backstage class
  • 35:03 - 35:05
    and it Flogs to 12, close to the 15,
  • 35:05 - 35:07
    and the average complexity of the set of
  • 35:07 - 35:11
    classes in that final solution is seven.
  • 35:11 - 35:14
    And so, I contend to you, the complexity
  • 35:14 - 35:20
    has fallen by 75% because
    I made many small objects.
  • 35:20 - 35:21
    And so now I'm going to circle back around
  • 35:21 - 35:23
    to my task, implement Conjured.
  • 35:23 - 35:30
    Take a minute and imagine how to do it.
  • 35:30 - 35:35
    There's a code that
    makes all the tests pass.
  • 35:35 - 35:38
    Here's how to use it.
  • 35:38 - 35:41
    And now we're done.
  • 35:41 - 35:45
    Alright, so, summary.
  • 35:45 - 35:48
    When you are new at
    this, they told you DRY.
  • 35:48 - 35:50
    Right? Don't repeat yourself.
  • 35:50 - 35:51
    And I'm not saying it's bad,
  • 35:51 - 35:53
    and I'm not saying that
    duplication is good,
  • 35:53 - 35:54
    but I'm telling you that if your choice
  • 35:54 - 35:57
    is between duplication
    and the wrong abstraction,
  • 35:57 - 35:59
    you should choose duplication.
  • 35:59 - 36:01
    Trying to fix a problem by increasing
  • 36:01 - 36:04
    the complexity of the wrong abstraction
  • 36:04 - 36:06
    is like chasing a beach
    ball in the outgoing tide.
  • 36:06 - 36:08
    Every time you take a stroke,
    it recedes ahead of you,
  • 36:08 - 36:11
    and pretty soon, you're
    out way over your head.
  • 36:11 - 36:14
    It's very hard to fix those problems.
  • 36:14 - 36:16
    Next, don't try to get to the future.
  • 36:16 - 36:18
    Open/closed, the right code that can
  • 36:18 - 36:21
    adapt to the future when it arrives.
  • 36:21 - 36:23
    New requirements, this
    requirement to implement Conjured
  • 36:23 - 36:26
    was the impetus to make a change.
  • 36:26 - 36:30
    It gives you the information
    you need about how
  • 36:30 - 36:32
    to make a choice about how
    to rearrange your code now,
  • 36:32 - 36:36
    so that you can do the next thing.
  • 36:36 - 36:39
    Kent Beck has a wonderfully
    succinct way to put this.
  • 36:39 - 36:43
    He says, "Make the change easy,
  • 36:43 - 36:45
    "and then make the easy change."
  • 36:45 - 36:47
    He actually put it a little bit longer.
  • 36:47 - 36:51
    He said, "Make the change
    easy, this might be hard,
  • 36:51 - 36:52
    "and then make the easy change."
  • 36:52 - 36:56
    And so, we spent 99% of this
    talk making the change easy,
  • 36:56 - 37:00
    and then it took one slide
    to make the easy change.
  • 37:00 - 37:03
    At the core of this, at the
    underpinnings of all this
  • 37:03 - 37:05
    is the idea of making small objects,
  • 37:05 - 37:08
    making objects that had
    a single responsibility.
  • 37:08 - 37:12
    And finally, trust the principles
    of object-oriented design.
  • 37:12 - 37:15
    They let you predict the consequences
  • 37:15 - 37:18
    of your code arrangement choices,
  • 37:18 - 37:20
    and learning something about
    what those consequences are,
  • 37:20 - 37:23
    is going to let you raise your game.
  • 37:23 - 37:25
    Metrics are useful, but they're fallible,
  • 37:25 - 37:27
    but opinions are no more precise,
  • 37:27 - 37:30
    so use metrics to give you
    another body of information
  • 37:30 - 37:32
    about how complicated your code is,
  • 37:32 - 37:35
    and then learn the rules
    of object-oriented design
  • 37:35 - 37:39
    so that you can choose which
    direction you want to go in.
  • 37:39 - 37:43
    Intermediate refactorings often
    make code more complicated,
  • 37:43 - 37:44
    but if you know the rules, you can
  • 37:44 - 37:47
    trust yourself to work through complexity,
  • 37:47 - 37:50
    and finally reach more
    open/closed code that's simpler,
  • 37:50 - 37:54
    and smaller, and that lets
    you have straightforward,
  • 37:54 - 37:57
    changeable, beautiful code.
  • 37:57 - 38:02
    I'm Sandi Metz, I wrote this book,
  • 38:02 - 38:05
    I'm writing this book,
    (laughter)
  • 38:05 - 38:07
    It'll be in the slide deck.
  • 38:07 - 38:07
    I'm teaching in London.
  • 38:07 - 38:09
    There's a public course in
    London coming up in June or July,
  • 38:09 - 38:12
    in case you're from over there.
  • 38:12 - 38:14
    Thanks to you all, and
    thanks to Jim Weirich
  • 38:14 - 38:16
    who gave me this Kata.
  • 38:16 - 38:27
    (applause)
  • 38:27 - 38:44
    (jazzy music)
Title:
RailsConf 2014 - All the Little Things by Sandi Metz
Description:

more » « less
Video Language:
English
Duration:
38:47

English subtitles

Revisions