IntroductionWelcome| 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 RefactoringWhat 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 RefactoringPreparing 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 RefactoringMove 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 RefactoringMoving 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 RefactoringRefining 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 |
|
|
ConclusionNext 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 |
|
|