navigate site menu

Start learning with our library of video tutorials taught by experts. Get started

Foundations of Programming: Refactoring Code

Foundations of Programming: Refactoring Code

with Simon Allardice

 


Refactoring is the process of taking existing code and improving it. While it makes code more readable and understandable, it also becomes much easier to add new features, build larger applications, and spot and fix bugs. In this course, staff author Simon Allardice introduces the formalized, disciplined approach to refactoring that tells you exactly what to look for in your code, and how to fix it, through a series of "code smells"—clues that let you look at a block of code and realize when there's something wrong with it.
Topics include:
  • What is refactoring?
  • Recognizing common code smells
  • Simplifying method calls
  • Making conditions easier to read
  • Using refactorings at the method, class, and application level

show more

author
Simon Allardice
subject
Developer, Programming Foundations
level
Intermediate
duration
1h 44m
released
May 15, 2013

Share this course

Ready to join? get started


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:04 Hi, I'm Simon Allardice, and welcome to Foundations of Programming, Refactoring.
00:09 This is a course on how to take existing code in whatever programming language you
00:13 have and make that code better. Okay, that's a loaded word, what do I
00:18 mean, better? Well, I don't mean faster, I mean
00:21 better-structured, better-built. Yes, more readable, and because of that,
00:26 more understandable. Your code is easier to work with.
00:29 It's easier to add new features, easier to spot and fix bugs.
00:32 And it's a way to stop your code gradually getting out of control.
00:36 But refactoring is not a vague command to just tidy your code up.
00:40 No, this is a formalized approach, a series of independent bite size techniques.
00:45 Clues that will let you look at a block of code and identify how it can be improved.
00:49 Exactly what to look for and exactly how to fix it.
00:52 Many of the most common programming IDEs like, Visual Studio, Xcode and Eclipse
00:56 will actually help you do many of these techniques.
01:00 After learning even a few of these refactoring techniques you will find
01:03 yourself naturally writing more modular code.
01:06 That your object oriented code, of knowing the correct place to put a piece
01:09 of functionality or a piece of data. It's like having a step by step approach
01:13 to adopt really good programming habits. So let's get started.
01:17
59:59 (MUSIC).
Collapse this transcript
What you need to know
00:01 As with most of my foundations or programming courses I want you to find
00:03 this one really useful across many different languages.
00:07 But that presupposes a couple of things, first that you can already program in an
00:11 object or into a language. I don't really care which one, but we're
00:15 doing a course here all about improving existing code.
00:19 So, you need to understand the code your intending to improve.
00:22 Refactoring is something you can only do to code you can already read, write and understand.
00:24 Now I say, object oriented language because while there are many of these
00:29 refactoring techniques that apply to a non-object oriented language.
00:35 Like straight C, or to something a bit more casual like informal javascript.
00:41 These techniques really shine in a substantial object oriented application.
00:45 So yes. And this goes, I will unashamedly throw
00:49 around terms like class, object, method, parameter, super-class, sub-class,
00:54 inheritance, polymorphism, and several more.
00:59 There are many techniques that deal with refining your classes and your class
01:03 hierarchy, but I'm going to make this relevant for as many languages as possible.
01:08 Now as you might imagine, I will need to show some code from time to time.
01:12 And rather than jump between a dozen different languages, I'm typically going
01:15 to use Java. This might sound like personal
01:17 preference, but really isn't. I don't program in Java anymore; I
01:21 haven't for over ten years, but java is simply the most generic language I could
01:25 use for this. If you're a C++, C sharp, objective C
01:28 programmer, you shouldn't have any problem reading Java, even if you've
01:32 never written a line of it. And I would wager, more Ruby and Python
01:36 programmers are comfortable enough reading a C style language than the other
01:39 way round. That doesn't mean you need to an expert
01:44 on Java syntax, that's not neccessary, it does mean that I could bring up some code
01:48 like this. And you would understand this is a method
01:52 called calculateDiscount, that takes in a parameter, performs some simple
01:55 operations, and returns the result. But all along, we're going to be focusing
02:01 on the concepts, it's not about the syntax here.
02:05 So you should be easily able to apply these ideas, in whatever language you choose.
02:10 With the same end in mind, I will always use as simple an example as I can to
02:13 illustrate any of these techniques. And that means you should fully expect to
02:18 see some really straightforward code, say, a method that takes two values, adds
02:22 them together, and returns the result. And sure, we wouldn't really be worried
02:27 about improving an introductory level program like that.
02:31 But if I can illustrate a technique here using very simple code, then I will.
02:36 Conversely, if there is something that only makes sense with a complex inherited
02:41 situation well then, we'll use a complex inheritance situation.
02:45 But with that in mind, let's cover exactly what refactoring is and clear up
02:49 the common misconceptions people have about it.
02:53
Collapse this transcript
1. Introduction to Refactoring
What refactoring is
00:00 Refactoring is the name we give to a collection of small, independently
00:03 learnable techniques for improving code that already exists.
00:08 Meaning this code is already written, and it's already working.
00:12 We are not changing what it does. Now these individual techniques, each one
00:16 called a single refactoring are often very small.
00:20 They can be described in a few minutes. And they each deal with one specific
00:24 common issue, and the most likely way to, well, fix it, is not the best word
00:28 because it's not broken. They deal with the most likely way to
00:33 improve it. Now each of these different techniques
00:36 has a name. There is the extract method refactoring,
00:39 the replace array with object refactoring, the remove middleman
00:43 refactoring and many more. And the idea is that you build a personal
00:48 library of these techniques, like having a personal programming improvement toolkit.
00:54 Different tools for different situations. Tool kit is actually not a bad analogy
00:59 for this. Like working with tools, there's no
01:02 particular order to this, no fixed one, then two, then three path for using these
01:05 refactoring techniques. Just as you wouldn't always use a hammer
01:10 first, and then a Phillips head screwdriver, and then a quarter inch
01:13 socket wrench. You just want them all available if you
01:17 need them. So, it doesn't remove your brain from
01:20 this process. This is programming, there is no such
01:23 thing as a set of rules that just anyone can follow to turn bad code into good code.
01:28 But it is a set of techniques to help you recognize these issues as long as you
01:32 bring your consciousness and your experience with you.
01:36 Refactoring techniques applied without thinking, will hurt more than they help.
01:41 Just because you have a hammer, doesn't mean everything needs to get hit.
01:46 So, how many are there? Well depending on what source you take as
01:49 gospel, and we will talk about that, there are between 75 to 100 or so
01:53 different well-defined refactorings. But like most things, it is a smaller
01:59 subset that are the most useful and that's what I'll focus on in a course
02:02 like this. The 30 or so most general, most useful refactorings.
02:09 The equivalent of the hammer, screwdriver and allen wrench tools you always want in
02:15 your toolkit. Rather than the equivalent of the
02:19 specialized 1983 RX7 rotary engine pilot bearing remover and engine down pin
02:24 puller tool. Yes, specialized and rare refactoring
02:28 techniques do exist, but we want general purpose here.
02:32 Now if you've been writing code for awhile, you've almost certainly kind of
02:36 done some of this already. If you've ever looked at a method that
02:39 was getting a little too long. Found a few lines in it that belong together.
02:44 Separated those out into their own method.
02:46 And then replaced the original code with a call to that new method.
02:50 Well, that's a refactoring, that is the extract method refactoring.
02:54 doesn't mean that just because you've done that kind of thing once or twice,
02:58 you've been doing refactoring all along. Because using the formal refactoring
03:03 technique, brings more conscious awareness to that decision.
03:07 It doesn't replace your intuition, but it gives you some more questions to ask.
03:12 If you're trying to break apart some code, what should you really be looking
03:15 for in this situation? What do you do when there's a mixture of
03:19 local and non local variables in the original code?
03:23 And are there any contra-indications, any reasons why you might not break out some
03:27 code into its own method? And that's the kind of thing we'll go
03:31 through for each of these. No single technique is the master refactoring.
03:37 It is the cumulative, additive impact of many small changes done consistently that
03:41 is the real improvement here.
03:44
Collapse this transcript
What refactoring is not
00:01 Just as important, as you start to get a sense of what refactoring is, is that you
00:05 stay very aware of what refactoring is not.
00:09 And it's easy to get the wrong ideas because we're saying things like we're
00:12 improving code. And that could imply all sorts of
00:16 possible meanings. So, three things that refactoring is not.
00:21 One, refactoring is not debugging. Your code already needs to work.
00:27 Either on the small scale, you have some classes and methods that are already
00:30 functional, testable and working. Or on the large scale you have an entire
00:34 shipped application that's already in use.
00:37 But refactoring is not a method for a bug hunt.
00:41 It's not a way to find either large showstopper bugs, you should have fixed
00:44 those as soon as they happened. But nor is it a way to find those pesky
00:48 now you see em, now you don't heisenberg that turn up from time to time.
00:54 There are ways to find these. There are ways to find these.
00:57 There are profilers, debuggers, test suites, refactoring is not one of those ways.
01:01 So if you're ever tempted to think, oh, I'll use refactoring to find and fix
01:05 those deep difficult bugs. Well, no, you won't because none of these
01:09 techniques fix bugs. They all assume your code already works.
01:13 Now it's true, during the process of refactoring you may notice something that
01:17 you realize is a problem. But the moment that you start to deal
01:20 with that. You have stopped refactoring and you have
01:23 started bug fixing which is a very different mindset.
01:26 Number two. Refactoring is not performance.
01:31 This is another very common misconception.
01:33 That we do this to improve our code. We're cleaning our code up and that's
01:37 going to make it faster. No, code performance is not the goal of
01:41 refactoring nor, let me be quite clear here, is it even an expectation.
01:48 Refactoring might even make your code technically slower because we're making
01:52 decisions about the construction of code, unlike building construction.
01:57 The decisions you would make to build a house as well as possible are not the
02:00 same as the decisions you would make to build a house as fast as possible.
02:05 Improving pure performance is something we do in software development but that is
02:09 a separate process with a different set of questions and a different set of tools.
02:16 But there is one thing that gets faster with refactoring, us, we do, programmers.
02:21 We'll be able to read and understand it, not just individual pieces of code but
02:25 the bigger application and that can lead to many benefits.
02:30 Well re-factored code will make us faster.
02:32 It is not intended to make the code faster.
02:34 And finally, number three, refactoring is not adding features.
02:40 If during this process you add a new feature, you support and you use case,
02:44 you add a single tiny menu item to an application, you are no longer refactoring.
02:50 Because in refactoring we do not change the behavior of the code.
02:54 Okay, behavior. Another hazy, nebulous word in programming.
03:00 So let's add another word to make this clearer.
03:02 In refactoring we do not change the observable behavior of the code.
03:08 We might be doing a lot internally, creating new classes, moving
03:11 functionality from one place to another, renaming, reorganizing.
03:17 But the end result of us doing the most perfect, most painstaking refactoring
03:21 process is that any end user who was using our application beforehand would
03:25 have no idea we did anything at all. We did not fix bugs, we did not make it
03:30 faster, we did not add features. We didn't change the observable behavior
03:35 of the code. So why do it at all?
03:39 Well, let's talk about that next.
03:42
Collapse this transcript
Why to use refactoring
00:01 We must be careful here. There is a misunderstanding common in
00:04 management but often found in programmers too that we refactor purely from some
00:08 perfectionist fixation on aesthetics. That the code must be perfect and elegant
00:13 and beautiful just because. There's no actual point.
00:17 It's like spending time polishing the underside of a table that nobody will
00:20 ever see. It's purely for our aesthetic
00:23 satisfaction, and it's not that at all. We always re-factor for a reason.
00:28 We always re-factor for a reason. Yes, we need to stay conscious, the
00:33 refactoring is not adding features, refactoring is not finding bugs,
00:37 refactoring is not about performance, but we often refactor simply because we want
00:41 to do those things. Because well-structured code will make it
00:46 so much easier to add new features and new capabilities.
00:50 It will make it easier to start analyzing performance.
00:53 So we're refactoring to get the future roadblocks out of our way.
00:57 Now, it can often be difficult for teams to get approval to spend time refactoring
01:01 when it's not part of the culture, because the benefits aren't immediately apparent.
01:06 Oh, you're not adding features, you're not working on performance, so why bother?
01:09 We bother so we can do those things easier.
01:13 We can add features quicker next week, or next month, or next year.
01:17 So that we can bring new people onto the team and just point them at well-written
01:20 code, rather than spend three months having to explain everything about what
01:23 it does. And so that a year, or two, or three down
01:27 the line, we don't have code that's such a mess.
01:30 The only way we can deal with new customer demands is to say, scrap it all
01:34 and start again from scratch, which is almost always a stunningly mad idea, but
01:38 can seem the only way out when the code has been allowed to decay to such a point.
01:44 And there's never been any structure for how to bring it back which is what
01:47 refactoring provides. Now if you're still wondering, okay, but
01:52 how much really is aesthetics? Consider this, that if you don't have a
01:56 reason to re-factor, then you don't re-factor.
02:00 If I walk into a consulting engagement and find that as part of their
02:03 infrastructure, they have let's say, a custom Java application written in the
02:07 mid-90's and untouched since the first year of operation.
02:12 It works, it's fast enough. There's no conflict with any of their
02:15 other systems and no features that need to be added.
02:18 Well, this could be the ugliest code in the world and it doesn't need to be refactored.
02:23 It would be no reason to do so. Because we don't do it purely from
02:28 aesthetics, because refactoring brings risk.
02:31 Any change we make are with the best of intentions, but yes, we're changing code,
02:35 and we stay conscious that that can have unforeseen consequences, and we'll talk
02:38 about dealing with that a little later on.
02:42
Collapse this transcript
When to use refactoring
00:01 When you first learn about refactoring, it sounds like something you do as an
00:04 independent stage. You write an application and then later
00:09 on you refactor it. Well, you may need to do that from time
00:13 to time if you have an existing code base that's a mess, but the intention is that
00:16 you start to do this all the time. That as you write code, you refactor as
00:22 you go. Write a new class or some new methods,
00:24 check and test it, and then take a few minutes to scan and make sure it's as
00:27 well-constructed as it could be. I'm trying to apply a little more
00:32 formality than you might naturally do if left to your own devices.
00:36 Now, it's true that as you've started at this, you won't know all the techniques.
00:40 And some might not be as natural to you. So in a team environment, a great way to
00:44 really learn refactoring is with formal or even informal code reviews or paired programming.
00:50 Bringing multiple sets of eyes to the same code.
00:53 And if you don't do that, you really should.
00:56 Now, in projects and teams I've worked on, even with multiple levels of skill,
01:00 you find different people seem wired to spot different opportunities for refactoring.
01:06 Some people are excellent at almost intuitively spotting situations with
01:09 duplicate or nearly duplicated code, whereas others are naturally noticing
01:13 methods that just seem to be in the wrong place.
01:16 Refactoring gives you a great way to have those kinds of conversations, and come up
01:20 with answers that are more than just somebody's gut feeling.
01:25 Now, if you're a solo developer, you may not have that option available to you.
01:29 So you need to pay a little more attention to applying the individual
01:32 techniques, particularly as you encounter ones that you wouldn't naturally think about.
01:38 But here's the thing. We are not trying to get to some point
01:41 where we take every line of code and have to go around a hundred different formal
01:45 techniques to see which one applies. We don't do that at all, instead we're
01:51 going to see if our code smells bad.
01:54
Collapse this transcript
Code smells
00:01 In refactoring, although you will also hear this term in some agile software
00:04 development circles, we have the idea of a code smell.
00:08 Something about this code that just smells bad.
00:11 And we use it as an indicator we have something to improve.
00:15 Now, code smell might sound like a bizarre idea particularly if you're quite
00:18 new to programming. What on Earth could I mean by- Code that
00:22 smells bad, so let me give you guys a quick, non-programming equivalent.
00:28 If you are, or you know someone, who is very attentive to spelling, and
00:31 punctuation, and grammar. And you show them a page that's full of errors.
00:37 Everything that ends in S has an apostrophe, whether it needs it or not,
00:41 maybe even two apostrophes. The word incredible is spelled with three
00:45 d's in the middle. And capitalization is almost random.
00:49 And as they read it, you watch their face.
00:52 It's never just a pure intellectual identification of the incorrect usage.
00:57 Instead, it's an emotional, almost physical response.
01:00 Ew, ew! We can get, and we want, that same
01:06 response to reading code. To open up a code file, and something
01:10 about us will make us go Ew. It's a code smell.
01:16 And, these aren't purely subjective. They're not just random intuition.
01:19 There are specific code smells you can start to notice, to build your
01:23 sensitivity to. Okay, an example.
01:26 Duplicated code is perhaps the most fundamental code smell.
01:31 The one that most programmers already react badly to, even if they don't use
01:34 the term code smell. You see it and you realize, Doesn't
01:38 matter that this works, something is wrong here.
01:41 Another code smell is a very long method. Suddenly going from methods with five or
01:48 ten lines to one with 80, (SOUND), hm. The code smell doesn't tell us what to do.
01:54 We might split this up into multiple methods.
01:57 Or even split it across multiple classes. Or possibly even leave it alone after
02:01 closer examination but it is something that will cue us to look at this code a
02:05 little closer. So code smells don't tell us what to do,
02:09 but they tell us what to notice. Too many comments is another code smell.
02:15 There's nothing wrong with comments. But if you suddenly run into code where
02:19 everything now needs multiple lines of explanation for it to make sense, and it
02:22 didn't before, well, why? Why isn't the code itself clear and readable?
02:27 Is it in the right place, in the right context?
02:31 Now some code smells are a bit more subtle, a bit more involved, as we'll explore.
02:36 But what you find is that the code smells hugely narrow down the potential
02:40 refactoring techniques we might use to deal with them.
02:44 So as we go through these techniques, we'll go through the most common code
02:47 smells as well, and you'll start to improve your inner ability to smell that code.
02:52
Collapse this transcript
Origin of these ideas
00:01 So, where do these ideas and techniques of refactoring and code smells come from?
00:05 If they are being held up as best practices, well, who says so?
00:09 There are now a lot of books, magazine articles, seminars on refactoring.
00:14 But I would be remiss in this course to not talk about this one, Refactoring
00:18 Improving the Design of Existing Code, by Martin Fowler.
00:24 This is the seminal work on refactoring. The book that popularized it in the
00:28 general conciousness of software developers.
00:31 This is a book that a lot of developers have, and it's a book that a lot of them
00:34 have never finished. Although there's a perfectly acceptable
00:38 reason for that. This is not a book that's intended to be
00:41 read from cover to cover. Most of it is a catalog of refactorings.
00:46 A few pages on each technique. So it's more of a reference than tutorial.
00:51 Something you can grab to verify, how a particular technique is applied.
00:55 Now you certainly don't need the book for this course.
00:58 In a video course, I'm taking a different approach to get you started with
01:00 refactoring concepts and techniques. And a few tricks of the trade and things
01:04 to watch out for. But if you decide to explore these ideas
01:08 further, yes, this is the best single source to do it.
01:11 And there's also an associated website, refactoring.com, that described a few,
01:15 new techniques to the additional 75 or so described in the book.
01:21 If you are someone who's building their software development book collection.
01:25 This is certainly one for the keeper shelf along with books like, McConnell's
01:28 Code Complete, The Mythical Man Month, The K and RC Programming Language book
01:32 and so on. Now, other books now exist that focus on
01:36 refactoring in a particular language or a particular environment.
01:40 Refactoring in Ruby, or refactoring in .Net, refactoring in PHP.
01:45 But as I've already mentioned, I'm going to steer away from platform specific
01:48 refactorings here and keep things as general as I can.
01:52 Okay, we've spent long enough talking about the background, what refactoring
01:56 is, very specifically what it isn't, why to do it, when to do it, and where it
02:00 came from. So next up, how do we actually get
02:04 started doing it?
02:06
Collapse this transcript
2. Getting Started: Method-Level Refactoring
Preparing to refactor
00:01 As we're getting ready to refactor, let me take a very quick detour to talk about
00:04 automated testing. We're changing code and that brings risk.
00:10 The single best way to prove that the changes you make when refactoring do what
00:14 you intend. Only what you intend, and don't
00:17 unwittingly mess everything else up is to have a set of automated unit tests you
00:21 can run on your code before and after. So, using a test framework something like
00:27 j unit for java, n unit for .Net, rspec for Ruby and so on.
00:33 I won't be talking about the specifics of these here, because this is not a course
00:36 on testing. It's a course on refactoring.
00:39 But I can't stress highly enough the value of having something that will
00:43 quickly let you run a series of pre-written tests.
00:47 Get all okay messages, make your refactoring change and then run all the
00:51 tests again, getting the same messages. Removing your gut feeling of, well, I
00:56 don't think I broke anything, which is unreliable.
01:00 Or trying to prove success by doing lots of manual testing of the app, which is
01:04 tedious and unreliable. Sure, automated tests aren't a silver
01:08 bullet either. Nothing is, including refactoring.
01:12 But even having an incomplete set of tests is a vast improvement over nothing
01:16 at all. Now, there is no technique in refactoring
01:20 that will force you to use tests. It's simply the most reliable way to
01:24 prove that a change you made over here didn't break something else over here.
01:29 Now, automated testing all the way full test driven development or behavior
01:33 driven development strategies. These are extremely complimentary to
01:37 refactoring and something you should be looking at if you haven't been already.
01:41
Collapse this transcript
Grouping refactorings together
00:01 A useful way to think about the wave of refactoring techniques we're about to
00:04 dive into is to understand they can be grouped into several loose categories.
00:09 I'm going to present them the way I found useful.
00:11 But as you start to learn them, you'll probably develop your own preferences,
00:14 because you'll recognize they operate on different levels of your code.
00:18 Now what do I mean by that? Well, first, there are refactorings you
00:22 can using simply looking at an individual method by itself.
00:26 Not worrying about the entire application, not concerning yourself with
00:29 class hierarchies or design patterns. But just focusing on the one method right
00:34 in front of you. To begin with, we might ask, is this
00:38 method well-named? Do the parameters make sense?
00:42 Does it seem to use lots of temporary variables?
00:44 Is there any duplicated code here or is there just too much going on?
00:48 Might this method benefit from being split apart?
00:51 And other questions on that same level. But then there are refactorings where we
00:56 take a slightly higher view. We're still looking at the method but now
00:59 being a bit more aware of its context. Of the class that it's contained in and asking.
01:05 Is this method in the right class, does it even belong here?
01:08 Does there seem to be a lot of similarities between this method and
01:11 perhaps other methods in the same class? And, we can move up another level of
01:16 abstraction, not focusing so much on individual methods, but more on the
01:20 communication between different classes. Does class A seem to do nothing but use
01:25 class B's methods and properties? Whereas class B does nothing but use
01:30 class A's methods and properties. And if so why?
01:33 And I'm, even another level. We can start to see if we're using the
01:37 right kind of object oriented approaches for this type of application.
01:41 Are the clues that we should be using more inheritance and polymorphism or
01:45 indeed less inheritance and polymorphism. Because some refactorings are very
01:50 concerned with out class hierarchy, some times we'll realize that one of our
01:53 classes contains behavior or data. That really should be pushed up to its
01:58 super class, or maybe the other way 'round, it's in the super class, and it
02:02 needs to be down into the sub class. And you will see several refactorings
02:07 that appear to be the exact inverse of each other.
02:11 One refactoring technique has the name Add Parameter, but another, is Remove Parameter.
02:17 One refactoring is called, Replace Delegation with Inheritance, and there's
02:21 yet another that is, Replace Inheritance with Delegation.
02:25 And it is, after all, perfectly understandable that the technique you
02:28 need is sometimes the opposite of another one.
02:32 Back to the tool kit idea, sometimes you need to hammer a nail in and sometimes
02:36 you need to pull a nail out. So here's how we're going to go through these.
02:41 By starting at the runway level, focusing more on individual methods.
02:46 And we'll work our way up, gradually getting into those higher levels of abstraction.
02:51 So let's take a look at the first set of refactorings.
02:55
Collapse this transcript
Using the Extract Method refactoring
00:01 First up, the classic, almost commonplace refactoring.
00:05 Extract method. To look at some code, identify a few
00:08 lines that logically belong together and create a new method from those lines.
00:12 Now this is one that experienced programmers express a little skepticism
00:16 or surprise that. Wondering why would we need to text
00:19 somethings that's apparently so trivial and give it a full named technique.
00:24 The people comfortable with formal refactoring tend to this a lot more often
00:27 and with more structure to how they do it, let's take a look.
00:32 I've got a segment of code here, of a straightforward method that's creating an invoice.
00:37 Again, syntax is not important. Just getting the general idea is enough.
00:42 So I've got these three lines of code here.
00:44 They're creating the header for an invoice.
00:46 They belong together. They describe a short but logical
00:49 self-contained task. And to use extract method, we take them,
00:53 we create a new method, which I'll call "printInvoiceHeader." I'll copy and paste
00:57 those lines into that new method and I'll replace that old location with a call to
01:00 that new one. And we no longer need the original
01:05 comment as this new method is well named and it's far preferable to have code
01:08 that's effectively self-documenting. That's the most basic trivial example of
01:14 the extract method refactoring. It does get more involved but the aim is
01:19 for small fine-grained methods. And while I am showing a simplistic
01:24 example, it's perfectly fine to be extracting methods with only a few lines
01:27 in them as long as that makes logical sense.
01:30 If you're not used to very small methods, particularly true if you come from a
01:33 procedural language background where hundreds of lines at a time are certainly
01:37 quite common, try and force yourself to err on the side of smaller is better for
01:40 a while. One of the benefits is reuse.
01:44 The more specific modular methods you write.
01:47 The more opportunities you'll find for reusing that method in another location.
01:51 And if you use inheritance, it's much easier to override a method if that
01:54 method is defined very specifically. Now, the example was very easy here
01:59 because the code we extracted is as simple as it gets.
02:01 It performs a few operations and neither uses nor creates any variables.
02:06 So, next let's take a look at formalizing what we do when it gets a bit more involved.
02:10
Collapse this transcript
Extract Method with parameters and variables
00:00 So we're still using the extract method refactoring, but let's go back to this example.
00:04 And now I'm going to look at extracting this next section of code.
00:08 It takes the order object originally passed into this print invoice method and
00:12 uses it to print out some customer information.
00:15 Now to separate this out into its own method, we're going to need the new
00:18 method we create to accept a parameter, so that we can pass some extra
00:21 information in. Because the order object that we have
00:26 here is local to this first original method.
00:29 Now, there are two ways we can do this. One, as simple as possible, keeping the
00:33 code basically unchanged. So we'll do that first.
00:37 I'm going to create a new method called print customer details, and it takes an
00:40 order parameter. We'll copy and paste the code in from the
00:44 top method, and we'll replace the original location with the call to that
00:48 new method, passing in that order object. This is simple, and this is fine, but
00:53 it's really not as granular as we could make it.
00:57 Because this new method here Print Customer Details, we it's only concerned
01:01 about printing customer details. And if you ask me what a method called
01:05 Print Customer Details should expect as a parameter, it's a customer object not an
01:09 order object. So a better way would be this, going back
01:13 to the original version. We'll notice that we're using this
01:17 order.getCustomer to get the customer object.
01:20 And then calling getName on it. We're going to create the new method,
01:23 called print customer details. And this time, it will be defined as
01:27 taking a customer parameter, not an order parameter.
01:31 We'll copy and paste in the code but we'll make just a small change to it.
01:35 So rather than working with an order object it just goes directly to customer.getName.
01:39 And in the original location we'll replace that with a call to
01:43 printCustomerDetails but using order.getCustomer.
01:48 This way we're keeping this new method as small and finely-grained as possible.
01:53 And if we have the opportunity to reuse it in the future, we know that we can
01:56 just pass in a customer object here. We don't have to have an order object.
02:01 Now, the difference between these might seem very subtle, but it's the kind of
02:04 thing that rewards your attention when refactoring.
02:08 And the place you want to be a bit more careful.
02:10 Is if you're going to make a change to any parameter that's passed into the new method.
02:14 But, we'll see that in a little while. First, lets take extract method 1 step further.
02:19 In this next example, we're still working with extract method.
02:22 But, let's say we've already extracted a couple of simple methods like print
02:26 invoice header and print customer details, and now we hit this block of code.
02:31 We're going around an array, calculating the total amount of this invoiceaAnd
02:35 we're using a locally scoped variable, total amount, that's defined here at the top.
02:40 Now, as long as we can scan this current method and figure out that the only place
02:44 this variable is being used is in this block of code.
02:48 Well then we can just extract everything out and just use that variable
02:52 exclusively in this new method. The complexity would only come if that
02:57 variable was used later on in the first method, say to calculate a discount based
03:01 on the total amount of the order, in which case we'd need to return a value
03:04 from this new method. However, as we start to get into that
03:10 kind of question, what soon happens is our code smell nose might start twitching.
03:15 Asking questions like well, should it actually be our job here to calculate the
03:19 total amount of an order? Or should that behavior best be put in
03:23 the order object itself? But, okay, we're not quite there yet.
03:27 So, let's start asking exactly what code smells we should be paying attention to
03:32 when using the Extract Method refactoring.
03:36
Collapse this transcript
Common code smells for Extract Method
00:01 There's two main code smells, two main clues, to look for when using extract method.
00:06 First, there's the idea of a long method. And here's the problem.
00:10 Usually people want to know exactly how long is too long?
00:14 It's not about length, it's about meaning.
00:17 There are no rules that say, when you get to some arbritrary number of 15 or 25 or
00:21 50 lines of code that you have to have a new method.
00:25 But if you can split a method up into more finely grained methods, then do that.
00:30 Even if those methods only have a few lines in them.
00:32 And again, if the idea of little methods doesn't seem natural to you, and you
00:36 think it's hardly worth it to have three five-line methods.
00:41 You'd rather have one 15-line method that does three things, try it for awhile.
00:46 Because it only after you get into the habit of creating all these discrete,
00:49 very specific methods that you start to feel the real value of them.
00:54 Code smell number two is comments. If the method that you're looking at is
00:59 broken up into several different sections, each beginning with a comment.
01:04 Then these should be leaping out at you as potential targets for extract method.
01:09 When the question is not, does it have a comment.
01:11 Nothing wrong with comments. But rather, does it need a comment.
01:16 And use this as a rule of thumb. If a section of code needs a comment to
01:19 explain it, then instead of writing a comment, extract the method out and name
01:23 the method well. Because you need good names, name it by
01:28 what it does. The typical format of course is verb,
01:32 noun, print, header, calculate, discount, open, connections, save, file.
01:37 And notice as we go through these refactoring techniques that they
01:40 themselves use this format too. We have extract method, move field, add
01:46 parameter, replace exception with test. Among simple code editors, you'll do
01:51 these extract method refactorings by hand, copying and pasting your code from
01:55 one place to another. Now, that does leave you liable to
01:59 mistakes, so it's another benefit to having a test suite you can run before
02:02 and after. But you'll also find that several of the
02:06 most popular programming IDEs will help you perform refactorings and we'll take a
02:09 look at those in a moment.
02:12
Collapse this transcript
Using IDEs for the Extract Method refactoring
00:00 Modern developer tools can help us perform some refactoring techniques.
00:04 And in this video I'll show three different examples of using an IDE to
00:07 perform the extract method refactoring we just talked about.
00:11 One in Java, using Eclipse, one in objective C, with Xcode, and in C# using
00:15 Visual Studio. Now the point of this is not to teach
00:19 these tools nor these languages, it simply to show the common approach.
00:22 The similarities between environments in languages and show you what you might
00:26 expect from the different programmer tools.
00:29 so first up Eclipse. I got a simple Java file here.
00:32 Now if I scan my code, I might see a block, say this.
00:36 Print customer details or print invoice header, that I want to split out into its
00:40 own simple method. So, I can select that code and sure, I
00:44 could copy and paste it. I could create my own method signature,
00:47 but here's the easier way. There's a refactor menu in Eclipse.
00:52 And clicking that gives me, a whole bunch of different options.
00:55 Eclipse has a very good set of refactoring options for Java.
00:58 And we'll explore a lot of these different refactorings over the course,
01:01 although I will be talking about them as a general approach, not just for Eclipse.
01:06 But if I come down here, I have this option here called Extract Method.
01:11 Selecting that. It's going to look at the code.
01:13 It's going to give me the option to type in the method name.
01:17 because, it's not going to be able to figure that out.
01:19 Sometimes it can be a little hard to figure out what to call a tiny method.
01:22 And here's a great clue, if you're looking at the comment, in this case,
01:25 Print Invoice Header, that would be a perfect thing to call it.
01:29 We're trying to replace comments in a lot of cases.
01:31 So, I'm going to call this Print Invoice Header.
01:34 An down at the bottom here it's, giving me a preview of what the method signature
01:38 will be. An you notice that it's automatically
01:42 determined, that if I'm going to separate this out into it's own method, it's
01:46 going to need to take a parameter because it's using this order object.
01:51 So, it's automatically going to decide to do that.
01:54 I've got a few other options here, it's going to default to making this a private
01:57 method so it's not accessible from any other class.
02:00 It's up to you whether you want to change that.
02:02 I'm just going to go ahead and click okay.
02:05 So up at the top we have the call to print invoice header, and if I look a
02:08 little further down in the class, what I now have is my newly refactored extracted method.
02:15 Onto the next one using Xcode and objective C.
02:18 Similar kind of plan again where we're scanning our code.
02:21 The actual clue that we're looking for is a block of code that belongs together,
02:24 and one of the great clues is actually comments.
02:27 If you feel that a section of code needed to be commented to explain what it does,
02:31 that's a good candidate for extracting. So, here I have a couple of lines that
02:36 are apparently getting a file path. And let's say that this is code that I
02:40 could read and I'm not exactly sure what it does, so that's why we've commented it before.
02:44 Well, I'm going to extract this as well. So, I'll grab these lines, and we don't
02:49 have a dedicated refactor menu in Xcode, but if I go up to the Edit menu, we have
02:53 a refactor section. There's not as many options in Xcode, but
02:58 we do have that Extract, here, I open that up, it's going to take a look at this.
03:03 And it's going to understand that it seems to returning a string, or it needs
03:06 to return a string, that's what the NS string option is.
03:10 And, I'm also being asked to, basically, give this a meaningful name, in my case,
03:14 it's going to be Get File Path. Not quite as many options in this window
03:19 as we had in Eclipse, but it does do something a little different when I click Preview.
03:24 It doesn't just make the change, it's going to show me a visual diff here.
03:28 With the old version on the right-hand side, where the old code was, and then
03:31 showing you visually what the new code that's going to be created will do.
03:36 Getting the file path, finding the right location, and then returning that value.
03:41 I can scan through that, it all looks okay and I'll go ahead and click Save.
03:45 Xcode is one of these IDEs that allows you to take snapshots.
03:49 I'm just going to ignore that for now. And it'll go ahead and actually make the
03:53 changes into my file. So we now have this getFilePath method.
04:00 The original code has been replaced by a call to that new method.
04:05 And finally, C# using Visual Studio, very similar idea.
04:10 I've got a block of code that I want to extract into its own method.
04:13 So, I'll select the code and we can see that we have a very similar idea here,
04:17 that I can select the option to refactor this.
04:22 And extract the method, I can let it verify what it's going to do and make the change.
04:28 So, very similar outcomes even across different platforms, different languages,
04:32 different IDEs. Now, there are also many third party
04:35 tools particularly in the Visual Studio world that exist to extend your
04:38 programming IDEs. And provide even more automated
04:42 refactoring support. Though I suggest that that is not your
04:45 first concern. First, get comfortable understanding the
04:48 ideas, then comfortable refactoring with a regular code editor, and then worry
04:52 about what support your IDE has for this.
04:56
Collapse this transcript
The Inline Method refactoring
00:00 While Extract method is one of the most common area factorings you'll ever use.
00:05 We will also, occasionally, encounter the need for its exact opposite, the in-line
00:10 method refactoring. Literally, finding a call to a method
00:14 that adds no value, no additional modularity, no additional kyote/g, it's
00:17 simply not worth it. So in this straightforward example I have
00:23 a method here that's calling the method get value of PI located somewhere else in
00:27 our code. And all that does is return Math.PI,
00:32 typical way to get PI in Java. Now refactoring is sometimes accused of
00:36 extracting everything it possibly can into its own tiny method.
00:40 This one really is pointless. So to do this refactoring, it's nothing
00:44 more than taking the body of the method, in this case Math.PI.
00:48 And just directly using that in every location that method was originally being called.
00:54 And then Deleting the method, test and verify.
00:59 The one thing to watch out for is that the simple method you're intending to get
01:02 rid of is polymorphic, in that it may have been written in a very simple format
01:06 in the super class just so it could be overwritten in a subclass.
01:11 And if that is the case, you don't do this refactoring, you just leave it alone.
01:15 You can't overwrite a method that doesn't exist.
01:18 Essentially if there's any additional complexity, any, yes, but, or this just
01:22 doesn't really work exactly this way, then you just don't do it.
01:27 The entire point of inline method, is that the existing method call added no
01:31 benefit whatsoever. The only other occasional use for reason
01:35 for in-lined method kind of informally. Is in the case where you have a bunch of
01:39 original methods that are some what randomly and badly written.
01:42 So you have method 1, method 2, method 3, method 4.
01:47 In this case you might temporarily use in-lined method to bring them all in line
01:51 make sense of them. And then use extract method to break them
01:55 back apart into better refactor code.
01:58
Collapse this transcript
Refactorings that remove temps
00:01 Next up, I'll cover several refactoring techniques that all focus on the use of
00:04 local temporary vairables, or temps, in our methods.
00:09 In this video, I'll talk about two that try to minimize their use, and in the
00:12 next video, three that talk about clarifying acceptable times to use them.
00:17 Though in general, you will find that temporary variables are discouraged in refactoring.
00:22 While it's true they can introduce bugs, they're tricky to track down, that's not
00:25 the real reason. They are discouraged here, simply because
00:29 temps increase the temptation for us to write longer methods.
00:34 If you're holding useful pieces of data in local temp variables in a method, then
00:38 it becomes way too easy. That when we need to add some new
00:42 functionality, we'll just sneak our new code into an existing method because then
00:46 we'll have easy access to that temp. So, the first refactoring removes that
00:52 temptation by changing the temp variable to a small query method instead.
00:58 And this is know as the replace temp with query refactoring.
01:03 It's common to see code that looks something like this.
01:07 Where we create a simple temporary variable to hold the result of a single expression.
01:13 And then we'll use that temp variable in multiple places in our code.
01:17 In this case, we're figuring out the available balance for an account object
01:20 by just using a couple of instance variables already in this object.
01:25 And then reusing that temp in a couple of places.
01:28 So, to use the replace temp with query refactoring, we'll do two things.
01:32 First, we'll extract this expression to a simple method and second, replace any
01:36 usage of that temp variable with a call to that method instead.
01:42 But before we do this, we've got a tiny little bit of prep work.
01:45 We would scan through the code and make sure there's only one place we're setting
01:48 the value of this temp, that we're not changing it to mean something else later
01:51 on in the method. Now as another bit of (UNKNOWN) , if your
01:55 language supports the idea of adding a key word to enforce a constant or a final variable.
02:01 Meaning, this temp would only be allowed to be assigned to once, then a good way
02:05 to verify or test your assumptions is actually using that keyword.
02:09 In the case of Java would be final. To find it as final, recompile it and
02:13 retest it, making sure you don't get any errors and nothing else is trying to
02:16 change it. Now if that's all okay, then we go ahead
02:20 with the real refactoring which has two parts.
02:23 First we take the expression in this case it's balance minus pending charges and we
02:28 extract that out to its own method. Like a mini extract method refactoring.
02:35 And then, the second part is where we replace every existing use of that temp
02:39 variable with a call to that new method. So notice, we aren't just doing the
02:44 extract method refactoring here, we're not just moving the code out into a
02:47 method and setting a temp from that. We're removing all references to that
02:52 temp variable. And of course, removing the actual
02:55 original variable declaration to ensure it isn't accidentally used.
03:00 Now, but wait, you might say. Isn't this more inefficient?
03:03 Because if we created the temp the old way, we'd only have to execute the
03:06 expression once, but if we turn it into a method, we might be calling it many
03:10 different times. And yes, you're absolutely right, but
03:14 remember, the pure efficiency of the code is not our first goal in refactoring.
03:19 Clarity is. The likelihood is that a typical
03:21 expression you would deal with in this sort of refactoring is going to be so
03:25 undemanding, it wouldn't be noticeable at all, even having to call it several more times.
03:30 But if it is an intensive operation, an intensive expression, well you should
03:34 really be working on that later, after you've refactored using profilers.
03:39 And other tools to make sure you're not doing pointless, premature optimization.
03:44 And the real benefit is that by creating this as its own method, we will also have
03:48 use of it anywhere else in the class, which wasn't the case before.
03:52 As the original temp was scoped to the original method.
03:56 So, we won't be tempted to add more code to the original method just to have
03:58 access to that temp. So we make the set of changes and of
04:02 course, test it and verify it. And that's the replace temp with query refactoring.
04:08 But I said there were two refactorings that deal with removing temp variables.
04:12 Well, we actually did both of them there, because the second step by itself.
04:17 Where we just replaced a temp with an expression is technically known as in
04:20 inline temp refactoring. In an example, if we had code that was
04:25 similar to the last one, but we had already created a method that determined
04:28 the value. We're creating a temp here, from a call
04:32 to this method. And then just, reusing that temp.
04:36 In multiple places, well the inline temp refactoring is where we're replacing just
04:40 the use of this variable with a call to that method.
04:45 So, this actual refactoring is often used as part of the replace temp with query
04:48 refactoring but you can also do it on its own.
04:52 If you notice that you are just creating a temp variable from a simple method call.
04:57 Once again one of the best practices to go ahead with this, is if you're pretty
05:01 sure that you're setting the value of the temp once.
05:04 Then before you actually do the re-factoring you would set it as final
05:07 and recompile and retest, just to verify your assumptions here.
05:11 And if that works, just simply go ahead and replace all references to that temp,
05:15 with a call to that method, of course removing the original temp variable.
05:21 The modest use of temps by themselves isn't harmful, nobody is pretending that
05:24 they are. But by removing them you'll find it's
05:27 much easier to do other refactorings like extract method.
05:31 And to create smaller methods in general. But next up we'll talk about a couple of
05:36 situations where refactoring considers it quite useful to use temp variables.
05:41
Collapse this transcript
Refactorings that add temps
00:00 While the use of temps are generally discouraged, there are three refactoring
00:03 techniques that deal explicitly with perfectly acceptable times to use them.
00:08 Those are the split temporary variable, the introduce explaining variable and the
00:12 remove assignments to parameters refactorings.
00:16 These probably sound more intimidating than they actually are.
00:18 I'll cover all three in the next few minutes.
00:20 First, split temporary variable. Simply put, don't use the same temp for
00:26 different reasons. We write a little bit of code something
00:29 like this, where we're creating a temp variable.
00:32 Here, I've called it t, setting it to one value, in this case the price times the
00:35 quantity and then writing out a message. Then, later on, somewhere else in the
00:40 same method, we wouldn't want to reuse that same t variable and make it mean
00:43 something totally different. In this case, amount due.
00:48 Can you write this kind of code? Sure, there's technically nothing that
00:51 would stop you. This would work.
00:53 But it's confusing. If I'm reading your code, I'm going to
00:56 make an assumption about what a variable represents, based on how I see it first assigned.
01:01 I don't want it to have two or three different meanings based on where I am in
01:04 the method. So to fix is simple.
01:06 Just create another temp split them. But in preference name them meaningfully.
01:12 In this case we'll create a new variable called amount due.
01:15 And to be honest, we should give the first one a better name as well, and that
01:18 is the split temporary variable refactoring, just adding a little bit of
01:21 formality to something that a lot of programmers would do naturally anyway.
01:28 Next up, we have the introduce explaining variable refactoring.
01:32 This one's all about pure readability. The many refactoring's remove code.
01:37 This one may add a few lines. It's the idea that you introduce the temp
01:40 or even several temp variables to break a complex expression into more readable parts.
01:46 Let's say I have a complex if statement like this one.
01:49 Doesn't have to be an if statement. This is just a good example.
01:52 Now this is difficult to read, difficult to understand what this is doing without
01:56 pausing and slowly reading through the code to make sure that it does what I
01:59 think it does. I can't just scan it and read through it.
02:04 So what we do here is first do a little bit of set-up by creating some temp
02:07 variables to represent different pieces of this expression.
02:12 So I'm defining three booleans here. And I'm defining them as final, just to
02:15 verify that once created, they can't actually be changed.
02:19 That may not be an option in your chosen programming language.
02:22 But you should still be able to understand the point here.
02:25 I'm breaking out different parts of that existing expression.
02:28 Like the first line here being ordered on, got total greater than 99.
02:32 This according to my own business rules means that yes, people qualify for free shipping.
02:37 Then we've got a little bit of code that was in that if statement, which is
02:41 checking the status of a particular order item.
02:44 And checking to see that if it's greater than the desired quantity and finally, we
02:47 have a little bit of code, again extracted out of that if statement to
02:50 check that the billing address matches the shipping address that we have for the order.
02:57 This is the first part of the refactoring.
02:59 The second will be changing the original complex condition, and just making it a
03:03 lot more readable. So now as we're scanning this code, it
03:06 should be much easier to understand. If stock is available, and we have free
03:10 shipping, and the address matches, then yes, everything's fine.
03:14 So we're using this temps to add clarity to the expression.
03:18 In this instance we're assuming that the only reason we're adding these temps is
03:21 for clarity, so they shouldn't complicate other potential refactorings like extract
03:25 method, nor will they provide temptation for writing longer methods in general.
03:31 And the final refactoring is Remove Assignments to Parameters, meaning if you
03:35 are passing parameters into a method, be very aware of the impact of any
03:38 assignment, any change you make to those parameters.
03:43 Now I'm not going to spend a great deal of time on this one, because the impact
03:45 of this can be very different, depending on your language and the type of data
03:48 that you're passing in. Well, what we're trying to avoid is where
03:52 you might make an assignment that has an unintended impact.
03:56 The capability for confusion is always high, and that's what we're trying to
03:59 avoid here. So the first "if" statement is asking "do
04:02 I have as many units in stock that are actually desired?" And if that's not the case.
04:08 I'm going to go ahead and change the value of that integer by setting desired
04:12 equal to orderItem.getStockUnits. We are changing that parameter that was
04:17 passed in. Now, this is entirely possible.
04:20 It's just not a great idea to do this. In classic Java, where everything is
04:24 passed by value, if you accept in, an integer, you're getting a copy of that
04:27 value, so you can change it, and the impact would be invisible outside of this method.
04:33 But going down a couple of more lines into the item equals new order item, if
04:37 we're assigning to an object parameter. And that's what we're doing here.
04:41 We're actually changing the value of item to a new object.
04:45 That adds a bit more complexity. Because if you're not totally clear on
04:48 your own language behavior; either you or someone else reading your code may not be
04:52 sure whether this line has an impact only in this method or whether you are
04:56 actually changing the original object passed into this method.
05:02 The best practice is that if you pass parameters into a method then they should
05:06 always represent what were passed in and never be reassigned to mean something else.
05:13 So in the first case here, where we changed this, desired integer, well
05:16 instead of doing that we could either use a temp, or better yet, just use the,
05:19 inline temp refactoring, and avoid using a variable all together, so we're not
05:22 changing the value of that parameter. And in the case of creating a new item
05:29 object, or rather than reuse the reference that was passed in, just be
05:32 explicit and create a new temp object to make very clear what we're doing here,
05:36 leaving the original parameter alone. And that is the Remove Assignments to
05:43 Parameters refactoring. That if you pass parameters into a
05:46 method, they should always represent what was passed in, and never be reassigned to
05:49 mean something else.
05:51
Collapse this transcript
3. Class- and Condition-Focused Refactoring
Move Method
00:01 We've seen several refactorings that tidy up individual pieces of code inside our methods.
00:05 But we haven't yet asked the question is that code in the right place to begin with?
00:10 So in this section of the course, we're going to be focused more on those questions.
00:13 Stepping up to conscious awareness of our classes.
00:17 Rather than purely on the individual pieces of code.
00:19 But it's always a good thing to begin by using the previous refactorings, like
00:23 extract method, and minimizing use of temps, cleaning up at that level.
00:27 Because it's much easier to take smaller methods and move them around if we decide to.
00:33 So the most common refactoring in this situation is called move method.
00:37 It's exactly what it says, we take a method out of one class and put it in another.
00:42 But what everyone wants to know is when do you do this?
00:45 Or more specifically, when do you recognize that this should be done?
00:48 Because this is a challenge in object oriented programming.
00:52 Yes sometimes it's easy to tell where a method belongs.
00:55 But it can also be a very subtle decision, particularly when one method
00:58 interacts with multiple objects. So this is the first question we need to ask.
01:04 If we look at a method, and it seems to be more concerned with accessing data and
01:08 methods in a different class, than it is with it's own class.
01:12 That's the key clue. That's the key code smell.
01:16 If we're looking through the code and we're realizing that what we see is more
01:19 access of a separate object. In this case, we're in the customer object.
01:24 But we seem to be doing a lot with an order object.
01:26 Yes, there's one reference to this, but mainly this code is working with an order object.
01:33 Now, if you care about code smell names, this is typically referred to as feature
01:36 envy, a method that is more interested in reaching over into a different class,
01:40 accessing data in another location. And the overall approach to this is just
01:46 moving that method across into the new class, moving over the code, and
01:49 changing, obviously, the internal references to make sure that it's
01:52 referring to the right object. Now, I've heard people comment that
01:57 surely move method should be unusual because we figure this all out in our
02:00 object oriented design. But no, sometimes we just don't have
02:05 enough information or perspective then. Move method is a frequent vanilla run of
02:10 the mill technique. Moving methods between classes happens
02:13 all the time in refactoring. So the general method is this.
02:17 And as ever I'm taking it as rather you got some kind of automated testing going on.
02:21 Step one, we're going to scan and read the code.
02:24 We want to make sure that we're not making too many assumptions about what is
02:27 calling this method and we need to understand how we're going to call the
02:30 new method if it's created in a different class.
02:34 Step two, is being very aware that if the current class has a super class, or sub
02:37 class, make sure you're aware of any impact there might be on moving a method.
02:44 If the method that you're thinking of moving is being used polymorphically,
02:47 well, that might be a reason not to do it, or you'll realize there's even more
02:50 to this. If there's no additional issues there,
02:53 then we go to step three, create the new method in the correct class, you can
02:56 change the name of it if it makes more sense, it doesn't have to keep the same name.
03:01 And step four, you'll copy the code across the new method.
03:04 And back in the original code, start replacing the calls.
03:08 Now if you have a lot of code that accessed the original method, you could
03:11 even leave the original method there and just replace it's body with code that
03:15 delegates behavior over to the new method in the new class.
03:20 An then if you can, remove the original method and test it again.
03:23 So if that's the general approach, what else might you run into?
03:27 Well, here's a couple of questions. What if the method that you're looking at
03:30 moving, generally uses data from two or three different classes.
03:34 Well the question is, which one does it use most?
03:37 It should be there. Object oriented design is based on the
03:39 idea of keeping our behavior an our data together.
03:42 So the rule of thumb is whatever data the method uses most, it should be in the
03:46 same place as that data. Though one thing to be aware of here is
03:50 the possibility that if this method is using data in a different class, well,
03:53 the data might be in the wrong place. And this is another technique we'll talk
03:58 about shortly, that we might use the Move Field option to move the field into this class.
04:04 Rather than move the method into that class.
04:07 And sometimes you realize a method doesn't belong in the class it's
04:09 currently in, but you don't have a great place to put it.
04:13 And in that case, this might become the Extract Class refactoring, where you
04:16 create an entirely new class and move the method into it.
04:21 But most of the time, it'll be as simple as what I've previously described.
04:24 You're going to notice that a method is using too many features of another class.
04:29 Because move method is such a common refactoring, there are several code
04:33 smells that might alert us to it. I'll talk about three.
04:36 Feature envy is the main one, the one we've already seen.
04:39 This method is just too fascinated by a different class and it should probably be there.
04:45 But we also have inappropriate intimacy, that's where rather than one case of
04:49 feature envy, two separate classes seem to do nothing more than directly interact
04:53 with each other. Calling each other and using each others
04:57 properties and move method is often used to solve this one as well.
05:02 And finally, shotgun surgery. This is another code smell we may notice
05:05 that would require us to use move method. And we'll typically notice this when
05:10 making other changes. If when we add functionality every change
05:14 to class A seems to require us to also change class B a little and class C a
05:18 little and class D a little. Well, then, there's a chance we need to
05:23 be moving some methods into a more focused, more oriented place.
05:26
Collapse this transcript
Extract Class and Inline Class
00:00 This next refactoring is one that usually occurs later in the lifetime of a
00:04 project, the Extract Class refactoring. Because it's in the nature of classes to
00:09 grow, they start to take on more than their original intention.
00:13 So if you notice, as your project has grown, that your class has begun to
00:16 sprout new methods and new data. And it's beginning to be more difficult
00:20 to read and understand. You can take it and split it apart into
00:23 two classes or, perhaps, even more. Now, the code smell here There's often
00:28 just large class. Like the large method code smell, there's
00:32 no explicit number. No particular metric that determines if a
00:36 class is officially too large. It is a judgment call.
00:40 But like large method, if you think a class might be too large, it probably is.
00:45 And one thing in particular to watch for is that some of your data and some of
00:49 your methods in the class seem to belong together.
00:53 And that starts to suggest that your class is already acting like two.
00:56 In this case, what began as a simple customer class containing customer data
01:00 has grown a subset of address related data and behavior.
01:05 Multiple addresses, perhaps, address-only behaviors like validate address and look
01:09 up zip, data and behavior that belong together.
01:14 So we use the extract class refactoring. We make a new class, and we start to move
01:19 everything over. Now, we're not pretending there was no
01:22 kind of relationship, so we will need to make some kind of link between the two.
01:25 In this case, the customer class might be changed to contain a potential array of
01:28 address objects. So when we're doing the extract class
01:32 refactoring /g, we're really doing many other refactorings at the same time.
01:35 Move method, move field, and probably some cleanup along the way.
01:39 And of course, running our tests at each step.
01:42 So we have some proof that this is working.
01:44 And while extract classes and common refactoring it wouldn't be a refactoring
01:48 discussion if we didn't talk about the exact opposite situation, the inline
01:51 class refactoring. Now as with using the extract method and
01:56 inline method refactorings, we are likely to extract to create new classes, far
02:01 more than combine or put them in line. But in the case where a class might have
02:07 been created with the best of intentions, but just really isn't doing very much, it
02:11 may be worth combining whatever data and methods it has out into another class.
02:16 Personally, the only times I've ever had to use the inline class refactoring is in
02:20 a situation where I've used other refactorings that have moved so much out
02:23 of the class, there's really nothing left.
02:26 Perhaps one or two properties. In which case, it just made sense to move
02:30 that data out into the class that used it the most.
02:33
Collapse this transcript
Making conditions easier to read
00:00 In this video I'll cover three easy refactorings that all deal with
00:03 simplifying conditional logic. Those are the Decompose Conditional,
00:08 Consolidate Conditional expression and Consolidate Duplicate Conditional
00:11 fragments refactorings. They may have complicated sounding names
00:15 but they are all straightforward. Now to be specific, we are of course not
00:18 going to change our actual logic in our program but we are going to simplify our
00:22 ability to read it. First, Decompose Conditional.
00:27 This has some similarities to the introduce explaining variable refactoring
00:31 from earlier in the course. That was the one that introduced a few
00:35 temp variables to make a condition itself easier to read.
00:39 Now this one takes it a little deeper. You would look to do this refactoring
00:43 when both the condition itself is complex, and if the if and else sections
00:47 are also complex. So it's a bit more than we could solve by
00:51 just introducing a temp variable or two. So what we do with this one is we
00:55 decompose it. We break the entire conditional apart,
00:58 take the complex parts of the condition and from the if and else sections and
01:01 extract them all out into their own methods.
01:06 Making the condition both shorter and more directly readable.
01:09 So let's imagine I just came across this code that somebody else wrote.
01:12 As it's not particularly complex, we can read this.
01:15 We know it works. We can figure out what it does, what it means.
01:18 And we'll have to figure that out to do this refactoring.
01:21 But we are trying to remove the point where we would see this again in a week
01:24 or a month or a year and just need to pause for a moment and say "hang on, what
01:28 exactly does this do again"? What we're trying to stop is this speed
01:33 bump that occurs when you hit code like this.
01:35 So, I would pause here, I'd actually start to read through this and what often
01:38 happens is you might be tempted to comment it to make it clearer.
01:43 Or what I'm suggesting is wherever you're tempted to comment some code refactoring
01:47 it instead. So here, I first look at the condition.
01:51 We've got some "or"s and "and"s involved here.
01:54 I'd read it, I'd understand it. What this is actually doing is figuring
01:58 out, is the customer an important customer.
02:00 They have a status of P. Or is the order more than a certain
02:04 dollar amount and less than a certain weight amount.
02:08 I'm going to read and understand it, and I'm going to create its own small method,
02:12 in this case, a method called Larger Important, that returns a boolean.
02:16 And the code in this new method is just the code that was in that original condition.
02:21 Then I step on and I look at the first if block.
02:25 What this is actually doing is calculating a discountedShippingRate with
02:28 a maximum of 100. So I'll create that as its own method
02:31 called discountShipping Rate. And I'll do the same for the else block.
02:36 This one will be considered the normalshippingrrate.
02:38 And finally I go back to the original conditional, and change it to use those
02:42 new methods. Now, when we read this, when we're just
02:45 reading through the code, I suddenly have this if statement.
02:49 If it's a large or important order, we'll do discountShippingRate, otherwise, we'll
02:52 use the normal shipping rate. We haven't changed the logic.
02:56 That's still been included in very modular methods, but what we're doing
03:00 here, as we do in so many refactorings, is removing the speed bump.
03:04 We're just making it easier to read. We can drill down into every piece if we
03:08 need to. And that is how we would decompose a conditional.
03:12 Stepping on to the next one, we're going to use the Consolidate Conditional Expression.
03:17 This one is also uncomplicated. And if you or someone you work with, is
03:22 in the habit of writing code like this, where we drop into a method and we see
03:26 that the series of simple tasks with exactly the same result.
03:32 In this case, we're calculating shipping. But, we're asking a few questions.
03:35 If it's an employee or a platinum customer or a gold customer or has a
03:38 coupon, we're always going to return 0. Now, they won't necessarily be right next
03:43 to each other but you should be able to scan through the logic and understand
03:46 that there's a series of tests all with the same result.
03:50 We're just going to consolidate them to bring them together into a more readable,
03:53 logical unit. So this refactoring doesn't apply where
03:56 you have different results, it's only to the same ones.
03:59 We would take them, we'd separate them out into their own method.
04:03 In this case, we'd understand what this means.
04:05 It's really does this person qualify for free shipping?
04:08 And then we'd replace that original code with the simpler call.
04:12 That's it, that's the Consolidate Conditional Expression refactoring.
04:16 And finally is the Consolidate Duplicate Conditional fragments and this is
04:20 something you will run into very commonly, that you'll see in a
04:23 conditional expression. That there's the same piece of code that
04:27 appears again and again in every branch of the condition.
04:32 So here we're calculating the discountShippingRate and then adding it
04:34 to the order. Calculating the normal shipping rate and
04:37 then same way adding it to the order. Of course what we need to do is scan
04:42 through our code and verify that what appears to be duplicated code really does
04:46 have identical behavior. So if you find some method call in the
04:50 middle of a branch, you need to make sure there's not part of that branch is
04:52 affecting it differently. But the end result is what you might
04:57 expect if you notice there really is duplicated code in your conditional sections.
05:03 You just take it and you move it outside. And then not surprisingly, go back in and
05:08 just remove the original lines. So, three very simple approaches to
05:12 commonly encountered problems and conditions.
05:16 Ways of just making them slightly easier to read.
05:19 In the next movie, we'll see another approach to breaking our condition that's
05:22 a bit involved that's going to use polymorphicm instead.
05:26
Collapse this transcript
Replacing conditions with polymorphism
00:00 This next refactoring also deals with conditional although this one has a very
00:03 specific focus. In a situation where you have an existing
00:07 class hierarchy. Even a simple one like this, a bank
00:10 account super class with checking, saving and investment account sub classes that
00:14 each just add a little data. Well, if you ever see a conditional that
00:20 needs to ask about the type of an object, that's a bad sign.
00:25 So let's say this code is found in the account class.
00:28 Finding an if statement or a switch statement like this one that needs to
00:31 check the actual class type. And perform different behavior based on
00:35 that type. There's almost certainly something that
00:38 should be done here, based on object orientation, not on a condition.
00:42 I'm not saying it's impossible, you'd have a need to check the type of object.
00:46 But I've usually seen this kind of code written by procedural C or COBOL
00:49 programmers who haven't quite clicked with some of the core ideas of object orientation.
00:54 That a lot of what we do with objects should make this totally unnecessary.
00:57 If there is custom behavior based on a class type.
01:02 That behavior belongs in that specific class, not anywhere else.
01:06 So in this case, where we have different penalty behavior.
01:09 Calculating a penalty amount, based on the type of account that's an indicator.
01:13 This code should be moved into the subclasses.
01:15 But in this case, each subclass that has specialized behavior.
01:19 Should override the withdrawal method. We're effectively doing an extract method
01:23 refactoring of the relevant behavior, into each class.
01:26 And we'd rely on polymorphism to insure that the right version is called /g.
01:30 Now it's possible you could leave some default behavior in the superclass method.
01:35 Though if it is significantly different for each subclass, and if your language
01:39 supports it. You may even, in the original method,
01:42 decide to make it an abstract method. So this method must provided by every subclass.
01:48 Again, not all languages allow you to do this.
01:50 But if you run into a conditional that's checking the account type, you should be
01:54 looking at it with one raised eyebrow. Now, of course, this refactoring that
01:58 we've just done, replace conditional with polymorphism.
02:02 Does make the assumption that you already have a suitable class hierarchy in place
02:05 for you to move your methods into and what if you don't.
02:09 Well in that case we do a different refactoring and we'll cover that next.
02:13
Collapse this transcript
Replacing type code with subclasses
00:00 The previous refactoring relied on us having superclasses and subclasses
00:04 already in place for us to move our behavior around.
00:08 But, if you're squeezing all your functionality into one complex class and
00:11 not using inheritance, that doesn't mean you're immune to this problem.
00:16 In fact, the same clue. Having conditional statements that change
00:19 behavior based on a type of object even if they're not formal subclasses.
00:24 May be the indicator that you should be using inheritance.
00:28 Here's what I mean by that. Let's say you've begun with one account class.
00:32 And that class itself has gradually grown and now contains a data field that
00:36 represents an account type. Now in your code, you could represent
00:40 this a whole bunch of different ways. You could have enumerations, have just a
00:44 string variable and so on, and that hardly depends on the language.
00:48 I'm using a straightforward numeric option, just a private integer with some
00:51 aliases to represent the different account types.
00:55 And we can imagine that when each of these objects is instantiated from this
00:58 class, it is initialized with one particular type code.
01:02 Okay, there's nothing remarkable here. Because the existence of a type code
01:06 itself is not the real danger signal. Although we could argue it could be
01:10 changed into a formal system wide enumeration or even a basic class, the
01:14 real danger signal for this refactoring is the fact that we change behavior based
01:18 on that type code. So let me say that again, the existence
01:23 of the type code is not the real problem here, it's that we change behavior based
01:27 on it. And this is the clue that this class is
01:31 trying to do too much at once. Now, you're likely to find other clues
01:35 too when you see some kind of type code. It'd be very typical that some properties
01:40 in this class are only being used in combination with a certain type.
01:45 But this is the basic code smell that the refactoring we're looking for is replace
01:50 type code with subclasses. We're going to split this class apart
01:54 into multiple other classes. Now, you might be thinking, well, don't
01:58 we have this already? Isn't this just the extract class refactoring?
02:02 And kind of yes it is, but extract class is quite general.
02:07 And this is a very specific one. We are now going to extract into
02:10 sub-classes and almost certainly at least two sub-classes because after all, if
02:14 there isn't at least two different kinds of behavior, there's no reason to do this.
02:19 So from the original class, we would create new classes, one for each of the
02:23 existing time codes and then move the specific behavior into those classes.
02:29 And if you had noticed during your preparation that there was a data field
02:33 in the super class that was only used with a particular time code, you're
02:36 likely to move that field into the subclass too.
02:40 And before breaking things apart into subclasses, you should still be able to
02:44 ask normal object orientation questions to test your assumptions about this situation.
02:50 So, we're working with inheritance, so can we ask the is a relationship
02:54 question, that a checking account is a account.
02:58 A savings account is a account and so on. Those ideas should still make sense when
03:04 you're breaking your type code apart into subclasses.
03:08
Collapse this transcript
4. Data-Focused Refactoring
Moving and encapsulating fields
00:01 The refactorings we've covered up to this point have been loosely oriented more to
00:04 the behavior of our classes rather than the data.
00:07 Of course we can never really separate the two.
00:10 An object oriented languages, behavior and data is deeply intertwined, even at
00:14 the simple idea of having get or access methods that directly wrap around our
00:18 variable data. But just as the move method refactoring
00:23 we saw in the last chapter is one of the most common refactorings you'll ever do.
00:29 Then the move field refactoring, moving our data, is another one we'll need
00:33 almost as often. However, move field will commonly bring a
00:37 couple of other small refactorings along with it.
00:42 But we should really begin by comparing move field with move method.
00:47 As we explored in the last chapter, the key code smell for moving a method is
00:51 feature envy. That a method, say, here a method one,
00:54 seems far more concerned with accessing a completely different class than it is
00:58 with its own. And that would be an indicator that
01:02 method needs to be moved. However, if the method we're looking at
01:05 seems primarily interested with accessing the data in another class.
01:10 We could also ask, well, rather than the method being moved over there, should the
01:15 field be moved over here? Well, it depends.
01:19 See we have to take a larger view here. We can't make the decision about whether
01:23 to move a field, from just looking at one method alone.
01:26 We have to get a better idea of every method that would access a particular field.
01:31 So say we're looking at field one here in class B.
01:34 Well, if it turns out that taking a larger view of it, this data field is
01:38 primarily used by methods in one other class, then yes, we have a good contender
01:43 for move field. If on the other hand, field one was being
01:49 accessed by several different classes, none of which seem vastly more important
01:52 than any other, then it's not really a contender for moving.
01:56 And move field is exactly the refactoring you imagine.
02:01 We're simply moving the data over to the new class, and changing any references to it.
02:06 But here's the thing. Move field is a refactoring that's
02:08 usually easier to get a quick gut feeling about than move method.
02:13 Because the complexity with moving a method, typically arises, when one method
02:17 operates on two or three classes, without one being dramatically more obvious than
02:21 the others. An fields don't typically have that
02:24 problem, even when multiple methods of different classes operate on the same
02:28 data field. The field itself usually has a singular
02:31 reason for existence, and it's often clear where the field belongs.
02:35 So, the most common reason for moving a field is that you're moving behavior at
02:38 the same time. Either into an existing or a new class,
02:41 and you're just moving the field along with the behavior.
02:44 I'm going to cover two additional small but related refactorings to finish this video.
02:50 So when you do the move field re-factoring, if that field is currently
02:54 stored just as a publicly accessible variable.
02:57 And that is if your language supports that idea, then you should look at
03:01 creating getter and setter accessor methods for that field before you do the move.
03:07 And this is the encapsulate field refactoring, literally that's all this
03:10 one is, just provide accessor methods, for a field that doesn't have them.
03:15 And, if you can, make the original field private.
03:18 Most object-oriented languages have this idea, although it's implemented quite
03:22 differently between them. And while we're talking about the
03:26 Encapsulate Field refactoring, we might as well cover one more, the Self
03:30 Encapsulate Field refactoring. The only difference between the two, is
03:35 that the Self-Encapsulate Field refactoring recommends you first go ahead
03:38 and encapsulate the field providing your accessor methods.
03:43 And then you also change all your references to use the accesser methods.
03:48 Even the internal references in the same class, rather than ever accessing that
03:51 variable directly. Now, there is some debate over the
03:55 necessity of this one. But exclusively using accessor methods is
03:59 viewed as a best practice in many organizations.
04:02
Collapse this transcript
Working with data clumps
00:01 So what is a data clump? Well, this another code smell.
00:05 A data clump is when you have several items of data that just always seem to be
00:08 found together, whether they exist as part of a class or you notice that they
00:11 just keep being passed into different methods beside each other.
00:16 Now if that data clump is a subset of an existing class it may be a candidate to
00:20 extract it out into its own class particularly if there are methods that
00:24 deal exclusively with that little clump of data.
00:29 But we've already talked about extracting classes and some times you may have
00:32 already done this and still find these three or four fields that are just always
00:35 found beside each other. So we use two refactoring's as best
00:40 practices to deal. With data clumps.
00:43 And, here's what to look for. First, is the Preserve Whole Object refactoring.
00:49 Here's the situation. You notice that you're asking for several
00:52 values from one object. In this case, we're grabbing several
00:56 values from an order object and saving them in temporary variables.
01:00 And then we're just going to turn around and pass all these values along into a
01:03 method as separate parameters. Now, from some of the refactorings we've
01:07 done already, one thing we could do here is just get rid of the temp variables by
01:11 using the inline temp refactoring. But we're not going to do that.
01:16 We're going to take it one step further. Instead of passing these individual
01:19 parameters, we just passed the entire object.
01:22 Preserve the object, so we change the method so that instead of accepting these
01:26 primitives, it's going to accept that entire object and will grab.
01:31 The data values inside it. Because in general we like to make our
01:35 parameter list as short as possible as they tend to make our code hard to read
01:38 and sensitive to change. In fact it is another code smell is a
01:43 long parameter list. So we change that original call just
01:46 passing that single order. Now it is common to have programs resist
01:50 changing a parameter list to take a single object instead of several
01:54 primitive values just in case they may want to call this method using values
01:58 from a different object some day. But be careful here of another issue,
02:04 another (INAUDIBLE) speculative generality.
02:07 This is where code attempts to be overly flexible.
02:10 Because it's possible that at some undetermined point in the future,
02:13 something might possibly, maybe, come in useful and we better try to cater to that.
02:18 Speculative generality leads to vaguely-named methods and variable length
02:22 parameter lists and all sorts of optional items.
02:25 And it makes code difficult to read and understand.
02:28 If you know you need a piece of functionality by all means write it.
02:33 If you don't know you need it, don't write it.
02:37 Now the second refactoring for dealing with data clumps is introduce parameter object.
02:41 This is where you notice you have a data clump.
02:45 Meaning that the same several fields are passed together into multiple different methods.
02:49 But what you're passing in doesn't naturally correspond to an existing class
02:53 or perhaps the same data is even coming from different classes.
02:57 In here we've got three methods being defined, draw rectangle, draw lips, draw
03:01 star, and they're all taking the same series of parameters.
03:05 So in this case, instead of passing in multiple integers representing an x
03:09 position, y position, height and width of a graphical element to draw on the
03:12 screen, we would notice this and then create a new frame class.
03:17 The frame class having those attributes. And we would rewrite those methods just
03:23 to accept that parameter object. Now the kind of classes you'll often make
03:28 from data clumps are quite simple. Very typically they're things that nobody
03:32 thought to turn into a proper class during the design process.
03:37 But what you'll often find is that by making the data clump into a class,
03:40 you'll often see the opportunity for behavior that you can also move into that
03:43 class as well. For example, we might start adding
03:47 methods to this frame class to move it or rotate it.
03:51 But even without that there is a benefit to minimizing our parameter lists and
03:55 formalizing these arguments. Consistency and clarity and that's what
03:59 we're after.
04:01
Collapse this transcript
Simplifying method calls and parameter use
00:01 These next collection of refactorings all deal with calling methods and passing
00:04 parameters into those methods. And they're small.
00:08 We'll cover six refactorings in this one video.
00:10 First up, rename method. And you might think, really?
00:15 Do we have to have this as a formal refactoring?
00:18 Well, yes. If you're using extract method and
00:22 replace temp with query or decompose conditional and so many, many others,
00:26 that encourage us to refine our code into small, often tiny methods.
00:32 Well, the entire point is that those methods instantly tell us what they do.
00:36 If you're not naming the methods well, why bother?
00:40 And we support rename method as a formal refracturing to make it explicit that we
00:44 expect to do this. Having to rename a method doesn't mean we
00:49 made a mistake. We're intending to refine and reorganize
00:52 our code to extract and combine behaviors, our method name should be
00:55 changing as our code changes. It's just as important to revise and
01:00 revisit method names as anything else in refactoring, even if the actual change
01:05 itself feels more trivial. Now, sometimes it can be a bit of a
01:10 challenge to name a tiny method well. Beyond the usual verb noun format of
01:16 calculate price or open connection, explode spaceship.
01:22 One of the suggestions in Martin Fowler's refactoring book, is that you just ask
01:25 yourself how you would have commented this code and then use that as your
01:28 method name. Long method names aren't really a problem
01:33 and they usually don't even impact typing.
01:36 Most modern IDE's will help you find an auto complete along method name.
01:40 But another great thing about modern programming tools is that the I.D.E's
01:44 that support refactoring are often very good at renaming methods.
01:49 And do a much better job of reliably finding any reference to that method
01:52 across multiple code files, much better than you might find with a regular find
01:56 and replace. So if you do have a refactoring menu in
02:01 your IDE, make sure to use it when you're renaming your methods.
02:06 And next up is the remove parameter and add parameter refactorings.
02:10 These are exactly what they seem. To add or remove a parameter from a
02:16 method signature. Now refactoring as a process tends to
02:20 prefer short parameter lists. So your personal aim should be removing
02:25 items from parameter lists far more than adding them.
02:28 Again, one issue here is the programmer tendency towards speculative generality.
02:34 You see sometimes see that you've changed a method so that it doesn't use a
02:38 particular parameter anymore but you don't want to delete that parameter just
02:41 in case. While the aim should be, that if you can
02:46 possibly remove a parameter, then do your best to remove it.
02:50 But, yes, it is sometimes necessary to add one, if you change behavior that
02:55 needs more data then, add a parameter to pass that data in.
03:01 Though be aware, that several of the refactorings we've done suggest
03:04 alternative ways to this, like Preserve Whole Object refactoring and the
03:08 Introduced Parameter Object refactoring. The main thing to be aware of when either
03:14 adding or removing parameters from a method is any impact you might have on a
03:18 sub class or super class, if that method is polymorphic.
03:23 Well lets move onto a specific refactoring that covers a good time to
03:27 add a parameter. The Parameterize Method refactoring.
03:31 And then we will cover, as we often do, its exact opposite.
03:35 So, we use this refactoring when you realize you have methods that do very
03:39 similar things and only differ in one or two values.
03:43 So, let's imagine we have a class called, TroubleTicket and we've got multiple
03:46 methods in here. Change status to new.
03:49 Change status to open. Change status to resolved and so on.
03:53 And let's imagine that each of these does almost identical behavior.
03:57 Perhaps they just time stamp the object. They generate an email.
04:01 They alter an internal status field. Well, this might be a perfectly
04:05 acceptable time to combine them into one method and actually add a parameter even
04:09 though we don't do that often and parameterize that method.
04:14 One method called change status and just pass in the new status here.
04:18 Now again, if the behavior of those previous methods was substantially or
04:21 even moderately different and you will only know by reading those methods, then
04:25 leave it alone. But that is parameterize method.
04:30 And the question is, what about the flip side of this?
04:33 This kind of situation where if we begin with a parameterized method but then we
04:37 realize it does do very different behavior based on the parameter passed in.
04:44 Well that calls for another refactoring. The reverse of Parameterize Method is
04:48 Replace Perimeter with Explicit Methods. Now the difference between the two should
04:54 not be vague. It should not be a matter of opinion.
04:57 Here would be the flashing alarm that tells you this one is worthwhile.
05:01 That as soon as this method begins, it completely forks behavior based on that perimeter.
05:07 There's immediately a big conditional, an if or a switch that branches into very
05:10 different behavior and there's little or no duplication between them.
05:15 And that's what you should be looking out for, for this replace parameter with
05:19 explicit methods. This will be the time to refactor this
05:23 out into separately named methods and prefer those instead.
05:28 And finally in this video the Separate Query from Modifier refactoring.
05:33 This is not a rule but another best practice here.
05:36 The idea is that if you have one method in your class that both alters the state
05:40 of an object and returns a value, in this case we have ChangeStatusandGetDetails.
05:47 Well, you should really consider splitting that into two.
05:51 changeStatus and getDetails. The idea being that your methods should
05:55 really have a singular focus. That while there may be the occasional
06:00 exception, if you have a method that you're using to return values.
06:04 Well you should be able to cull that repeatedly and always get the same
06:07 results without repeatedly changing the state of the object.
06:11 Yes, methods can modify objects and methods can return values.
06:16 But, generally speaking, avoid doing both at the same time.
06:20
Collapse this transcript
Pulling and pushing methods and fields
00:00 Next, instead of extracting and moving our methods and fields, we're going to
00:03 work with, pushing and pulling them. The four refactorings in this video, are
00:08 similar in scope, and they're all based on things we can notice, to refine a
00:12 superclass, subclass relationship. So, first the pull up method and pull up
00:18 field refactorings. The idea here is you have a class hierarchy.
00:23 And during your refactoring, you figure out that there is either a duplicate data
00:27 or duplicate behavior into or more subclasses.
00:31 So we pull it up. We take the field or the method, and we
00:34 move it out of the subclass and up into the superclass, and then delete any
00:38 duplication in the other subclasses. Now this is more common than you might
00:43 first think. It happens not from poor design, but
00:45 rather when the subclasses have been developed by different programmers over a
00:49 span of time. And they've both had to write in some new
00:52 behavior or some piece of data. And pulling up a field is typically
00:56 simpler than pulling up a method. Although the actual change is not that
00:59 bad for either of them because you're removing duplication.
01:02 The work is in reliably identifying that this is actually what's going on, that it
01:06 really is duplication in the sub classes. And it'd be nice and convenient if the
01:11 sub classes used exactly the same name for duplication, and often they don't.
01:16 You might find one sub class calls its field price and the other one calls it cost.
01:21 One's a float and the other's a double. In this case, the checking and saving
01:25 subclasses both had date open, so that was easy to pull up.
01:29 But the investment account has open date, which might be named differently, but is
01:32 really the same thing. So we can actually pull that one out,
01:36 too, we might have to do a little bit of renaming.
01:39 But the real issues is looking a little deeper in these subclasses to figure out
01:42 the meaning of this particular field with that particular method.
01:47 As you might imagine, this one is so much easier to do after you have already
01:50 cleared the decks with the lower level refactorings, like extract method.
01:55 And removing the temps and making your conditionals easier to read.
02:00 Now, if for the method, it's not exact duplication that's very similar, it may
02:03 still be worth pulling it up into the super class if it makes sense to do so.
02:08 So you can use the default behavior to find in the super class and then override
02:11 just the differences in the subclass. One benefit of this is if you pull up the
02:16 duplicate behavior into the super class, it'll be much easier to add another new
02:20 subclass later on. But on the other side of Pull Up Field
02:25 and Pull Up Method, we have the Push Down versions of the refactorings.
02:31 But be careful with these. It is very common to assume that these
02:34 are about overriding. About providing default behavior in a
02:38 superclass and alternative behavior in subclasses, and it really isn't.
02:42 See, the Push Down refactorings might as well be called Push out.
02:47 They are about pushing behavior completely out of the superclass so it's
02:51 only in the subclasses. It's not about polymorphism and
02:54 overriding at all. So the two Pull-up refactorings and the
02:58 two Push-down refactorings are often mentioned in the same breath, but require
03:02 a very different kind of attention. The key in this one is to identify when
03:07 there's currently a method or a field to find in the superclass that is only being used.
03:15 And only makes sense in one particular subclass.
03:19 In this case I'm saying that the account superclass has a method called issue ATM card.
03:24 That I'm saying with my business rules will only ever be used in the checking
03:28 subclass and it should be there. That method, doesn't make sense for the
03:34 other two subclasses. And because of polymorphism, this
03:38 situation can be quite subtle to spot. After all if a subclass is using a method
03:42 that was defined in a superclass, there's nothing to announce that fact, it will
03:46 just work. The other subclasses would have full
03:49 access to their method. They just didn't care.
03:52 This is a great starting example that, as we begin to work our way up in
03:55 abstraction, the choices get a little less obvious.
03:59 And we're going to continue in the next section with more refactorings targeted
04:03 at these higher level situations.
04:05
Collapse this transcript
5. Communication and High-Level Refactoring
Refining hierarchies
00:01 We've seen several refactorings that are concerned with reorganizing code in our
00:04 class hierarchies. They begin casually with refactorings
00:08 like, move method and move field. When we realize, it's just a more
00:12 suitable class to put some behavior or data in.
00:15 Then we move on to things like replace conditional with polymorphism, or replace
00:19 type code with subclasses. That helps us with a situation where
00:23 functionality could be achieved using explicit conditionals, but it would be
00:26 simpler and more future proof with polymorphing method calls.
00:31 And of course the pull up and push down refactorings we just covered.
00:35 But there are a few more refactorings that deal in the same area.
00:39 And the main ones here are almost a subset of others we've already seen.
00:42 First, there's extract superclass. This is a specific kind of extract class refactoring.
00:51 The situation is when we realize we have two or more classes with significant similarity.
00:57 So we're going to take what's similar out of those, make a new class.
01:02 Make it the super-class of these, and move all those similar features into it.
01:07 So this one really is a combination of extract class, then pull-up method and
01:11 pull-up field. The real difference here apart from the
01:15 obvious inheritance relationship is that the class you extract, this super-class
01:19 is not there to be instantiated. So if your language supports it you
01:23 should make it an abstract class so it cannot be instantiated.
01:27 It's purpose is to be a location for the common data and behavior.
01:31 The code smell we're trying to avoid here, is the most basic code smell there
01:34 is, duplicated code. Next up, the extract subclass refactoring.
01:41 Another specific kind of extract class refactoring, and while it's easy to think
01:46 of it as the extract superclass, it really isn't.
01:50 This is a peer relationship, not an opposite, and the thought process is very different.
01:55 You extract a subclass when you realize that you are instantiating multiple
01:59 objects from an existing class but there's a mismatch in behavior.
02:04 Some objects use all of the class, some objects only use part of the class.
02:09 The most subtle distinction in this one is realizing when you need the normal
02:12 extract class refactoring versus when you have the need for the extract subclass refactoring.
02:18 Well here's the best simplest way I can put it.
02:21 If you have an existing class and realize some objects use one part of the class
02:25 and some objects use a different part of the class.
02:29 That's a clue for the regular Extract Class.
02:32 You are separating those different parts, but here's the other case.
02:37 When you have some objects use one part of the class and other objects use all of
02:41 the class, that's the clue for extract subclass.
02:46 So not part versus part, but part versus whole.
02:50 And this should be no particular surprise.
02:52 If we have that first class we're going to create a new subclass leave the
02:56 behavior that's used by all of the subjects in the super class and move only
03:00 the behavior or the data that's only used by some objects out into that subclass.
03:08 A small but a very important distinction and one that shouldn't be done casually.
03:12 And third we have the collapsed hierarchy refactoring.
03:16 This is the true flip side refactoring of extract superclass, and extract subclass.
03:22 It's when you realize an existing subclass and superclass you have just
03:25 aren't that different, it's not worth the extra work of keeping them both around.
03:31 So we collapse the hierarchy. We combine the subclass and the
03:35 superclass together, remove one of them and then replace all references to that class.
03:40 The main challenge here can be figuring out which one to get rid of.
03:43 There is no rule that says it's always the subclass pull up into the superclass,
03:47 it could be the other way around. You might realize the superclass was a
03:51 case of speculative generality, someone wrote it to perhaps provide some possible
03:55 shared behavior. That behavior was never needed.
03:59 The most likely time you'll collapse a hierarchy, is after doing a bunch of
04:03 other refactorings, you end up with a class that just isn't doing very much anymore.
04:08
Collapse this transcript
Communication refactorings
00:00 When objects start passing other objects around, there are two very common code
00:04 smells, which each bring a closely related refactoring.
00:07 First up, the code smell of message chains.
00:11 This is the one where you'd get an object just so you can ask it for a different
00:14 object, so you can ask that object for yet another object.
00:18 And so on, 'til you finally get to the piece of data, or call the method that
00:22 you really want to get to. Sometimes it's written like this with
00:26 intermediate temp variables. We'd grab a customer object by grabbing a
00:30 method on the order, then grab an address object by calling a method on the
00:34 customer we just got. And then grab a zip by calling a method
00:38 on the address of the customer we just got.
00:41 And sometimes you'll see it just written as one big chain.
00:44 order.getCustomer, cust.getAddress, addr.getZip.
00:48 Now is this always a terrible, terrible thing?
00:51 Well no, but the problem is this code is fragile, and it is sensitive to any
00:54 change, to any of the objects in the middle.
00:58 So, if we decided it was a real business name to be able to ask the order object,
01:03 directly, for its zip code. Then what we'll do is we use this
01:08 refactoring code called hide delegate. That is the refactoring associated With
01:13 message chain. Now when we start talking about delegation.
01:16 It's one of those tricky areas where the language customs can have subtle or
01:20 substantial knock on effects. Objective C for example, uses formal
01:25 delegation where other languages use inheritance.
01:28 But I'm not talking about delegation here in any specific language.
01:33 I'm really just referring to the loose concept of delegation A relationship
01:37 between classes that is not inheritant. Simply we have one object asking another
01:42 object to do something on its behalf inheritant.
01:46 So in essence if I want to use the hide delegate refactoring in this situation it
01:50 just means that we understand there is some delegation going on here.
01:55 Objects are passing responsibilities to other objects.
01:59 But we just don't want that to be visible, we want to encapsulate or hide
02:02 that behavior. In this case, I don't care that the zip
02:06 is inside the address object, which is inside the customer object, which is
02:09 inside the order object. I just really want to ask the order for
02:14 it's zip code. I should be able to write order.getzip.
02:18 And what that would mean is writing a get zip method inside the order class, and
02:22 including the necessary behavior there. We wouldn't be moving fields, we wouldn't
02:28 be moving methods. Effectively, that delegation still
02:32 occurs, but we encapsulate its behavior inside the relevant object, so it's less fragile.
02:38 This is the Hide Delegate refactoring. Now, any change made to this chain can be
02:42 done once inside this new Get Zip method, rather than potentially multiple times in
02:47 these long, manual message chains. Now, does this carry it's own maintenance headache?
02:54 Sure. It may prove to be the wrong approach.
02:56 If only one piece of code was ever using that message chain.
03:00 It may not have been worth it to convert this into a method, but it is a technique
03:04 worth trying anyway. And then we have the middle man code smell.
03:10 Rather than a message chain, the realization that a class or certain
03:13 methods of a class seem to exist only to pass information back and forth between
03:16 other classes. And it otherwise has little or no
03:20 impactful behavior of its own. So let's say we've got a shopping cart
03:24 here that calls the getUnitPrice on LineItem.
03:28 And all that class does is turn right around, call getUnitPrice method on the
03:31 Product class. Receive the result and pass the result
03:36 right back. Well this is just too much encapsulated
03:39 delegation, this is middle man. This can be what actually happens if you
03:43 overdo the hide delegate refactoring we just talked about.
03:47 You might end up with multiple methods that do nothing else except pass a
03:50 message directly on. So this is the code smell of middle man
03:54 and the refactoring for this code smell. Simply called, Remove Middle Man.
03:59 I hardly need to tell you what that one suggests.
04:02 Though it is worth pointing out that it doesn't necessarily mean you're removing
04:05 an entire class from your application. The class itself may have other methods
04:10 that are worthwhile. Just that in this situation, we'll get
04:13 the first class to call that product class directly, and just remove that
04:16 method call in the middle.
04:19
Collapse this transcript
Larger scale refactoring
00:00 I began this course talking about the idea that these refactorings work at
00:03 different levels and started with the common low level techniques.
00:07 I'm going to finish by briefly exploring the idea of a large scale refactorings
00:12 beginning with convert procedural design to objects.
00:17 This is where you've inherited or even created an application that just isn't
00:21 object oriented at all. Well, you can't make many of the
00:24 medium-level refactorings work. Because they assume that you have at
00:28 least a reasonably simple class hierarchy, but you don't, because the
00:31 concept of object orientation just haven't been applied.
00:35 Now, this application is likely to be in an object-oriented language.
00:39 But if it's been written by programmers who don't quite get objects, you're
00:42 going to end up with a procedural design. Now the code smells here are not just
00:47 large classes, but what are often called god classes.
00:51 Classes that don't just do a lot, they do everything, methods with hundreds and
00:55 hundreds of lines of procedural code. Or if you have classes, they're just dumb
01:00 data structures, they're just data devoid of behavior.
01:04 But the problem of course is, can you just do a convert procedural design to
01:08 objects as a straightforward refactoring like doing an extract method?
01:13 Well, of course not. In a small application, converting this
01:17 may be even a fun exercise to turn into a proper object oriented application.
01:21 But in a large app, your first tool, here, for this refactoring is not your
01:25 code editor, it's going to be a white board and a lot of planning.
01:29 And it may take you a long time before you even touch a line of code.
01:33 Now there are a handful of other refactorings considered large scale, such
01:36 as the separate domain from presentation refactoring.
01:40 This is the widely known best practice that you don't mix your business logic
01:44 with your user interface elements. Now I'm not going to cover this one in
01:48 any more detail because it is quite environment specific.
01:51 Most platforms these days do encourage this overall approach.
01:55 And they use designed patterns, like Model View Controller MVC, or Model View
01:59 View Model, the MVVM pattern. But these are no longer things we can do
02:05 in minutes, or even hours, we have stepped from the realm of turning bad
02:09 code into better code. And more into high level software
02:13 architecture discussions. And that, is where I'm going to draw the
02:17 line on this particular foundations course.
02:21 But having said that, the last thing I want to point out is this.
02:24 Even with the large-scale refactorings, the actual process of doing this will be
02:28 a collection of refactorings we've already explored.
02:32 It'll be extract class and extract method, moving methods and fields,
02:36 extracting subclasses and superclasses. Pushing methods down, and pulling them up.
02:42 Everything we do in refactoring, is always going to start with those basic ideas.
02:46
Collapse this transcript
Conclusion
Next steps
00:01 We've covered over 30 refactorings in a very short time.
00:04 And there are more to learn, but I suggest first that you don't.
00:08 Don't make the mistake of thinking that you need to know all the refactorings
00:10 before you can use any of them. In fact, quite the opposite.
00:14 To learn them well, it's much more practical to pick just a handful and
00:17 start to actively apply those. Don't worry that there might be a more
00:21 perfect refactoring out there. Worry about getting into the habit.
00:26 And as I've said several times and I'll say again here, start at the bottom.
00:29 Renaming and extracting methods, shortening parameter lists, minimizing
00:33 gratuitous use of temps, making your conditionals easier to read.
00:38 Start at that level and work your way up. Since it's often ideologically tempting
00:43 to start at the big picture. So before we touch anything, we better
00:46 think about how the overall design should be altered to make better use of design patterns.
00:52 In practice, that would be almost impossible to do until you clear up the
00:55 lower level stuff first and actually understand what you have, and where your
00:58 pain points are. And when you sit down to refactor, be
01:03 focused on the code smell first and the actual refactoring techniques second.
01:08 So when you open a project and you're looking at code for the first time, or
01:11 the first time in a long time, just scan through that part of it two or three times.
01:16 First, direct your attention to noticing length.
01:19 Long methods, long classes, long parameter lists.
01:24 Then scan it again, this time noticing any sudden conglomerations of comments.
01:29 Quickly scan through your multiple classes in turn and you will suddenly
01:33 notice one, if it explodes with switch statements.
01:36 What you're looking for in the first scan is not hey, here's exactly where I need
01:40 the decompose condition refactoring, or here's where I need the replace parameter
01:44 with explicit method refactoring. You're just looking for that hmm, that
01:49 little itch that tells you that one of these things is not like the other.
01:54 The places to bring your attention to. Oh sure, some code smells don't come from
01:59 scanning, they will require close, even very close attention.
02:04 Noticing data clumps or speculative generality, places to push down methods
02:08 of fields. And as you get even further, you'll go
02:11 from things that take a few minutes to refactor to things that take a few hours,
02:15 to things that take weeks or months to do correctly.
02:20 And if you can refactore with somebody else, even some of the time then try to
02:23 take that opportunity. And that's whether their skill levels are
02:27 way above yours or way below yours. And when you're comfortable with a scent
02:31 of refactorings and you want to take it further and explore more, then your
02:34 resources should absolutely include Martin Fowler's refactoring book without
02:38 which this course wouldn't exist. And the refactoring.com website, which
02:44 has short summaries of nearly 100 refactorings, including the ones in this course.
02:49 Now, those other refactorings aren't necessarily more difficult.
02:52 In fact, some of them are positively easy.
02:54 As an example, go and look for the Replace Magic Number With Symbolic
02:58 Constant refractoring, which takes about as much time to say as it does to learn it.
03:05 But you'll find most of them are just a bit more specific, than the general
03:08 purpose ones I've covered in this course. And as you get more comfortable, you may
03:12 also want to look for resources on refactoring in your chosen platform or
03:15 chosen environment. The principles are exactly the same.
03:19 But there are often platform specific tips and tricks.
03:23 So thanks for joining me for Foundations of Programming Refactoring, and good luck
03:28 making your code better. See you next time.
03:31
Collapse this transcript


Suggested courses to watch next:

Foundations of Programming: Fundamentals (4h 47m)
Simon Allardice


Foundations of Programming: Databases (3h 11m)
Simon Allardice


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 104,069 instructional videos.

get started learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 2,024 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.

preview image of new course page

Try our new course pages

Explore our redesigned course pages, and tell us about your experience.

If you want to switch back to the old view, change your site preferences from the my account menu.

Try the new pages No, thanks

site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked