- Refactoring is an important aspect
of test room development,
but it's often overlooked by developers.
Developers get carried away.
We write our tests, then we write our code,
then we write the next test,
then the next piece of code.
After writing a piece of code,
you could pause and consider
whether you can refactor your code to improve it.
So after each iteration of a test,
I ran the test, it failed.
I added the code.
I ran the test again.
It passed.
After each of those iterations, you should pause
and take a step back, review the code
to see if you can improve it, reorganize it
to make it better.
So some examples common refactorings
to eliminate duplication.
You might after awhile realize
that you've got the same code happening
here, there, and everywhere.
That's bad.
Refactor it into a common shared method
so that you don't have to maintain the code
in separate places.
Kind of obvious, really,
but you need to be disciplined to do it.
To restructure inheritance,
you might find that two classes, for example
are quite similar.
So instead of having two classes which overlap each other,
extract the common methods and properties into a super class
and use inheritance to avoid duplication.
And renaming variables is a common refactoring exercise.
So what we'll do in this section
is we're gonna go through the process
of refactoring some code.
So if you look in the refactoring folder,
here it is.
So in lesson seven
if you open the refactoring folder in the code editor
what you'll find is there are two folders.
There's an original folder
that contains some code that I've written.
And then there's a refactored folder,
which is kind of like before and after.
The code that I wrote initially
and then the improved code after refactoring.
So we are gonna look at,
I'm gonna use Visual Studio as well to help me
refactor certain things from that code
until it looks more like that code there.
So the original code and the improved code.
So there we go.
The original folder is my original product class.
And after the next five minutes or so,
we'll have an improved version in the refactored folder,
and I'm going to use, I'm using Visual Studio Code
as my editor.
Most editors have some, you know, at least some support
to help you refactor
so that you don't have to kind of rename things manually.
You can use tools in your editor
to do it more consistently and thoroughly.
It's good to use the refactoring capabilities of an editor
rather than doing like a global search and replace
which is more error prone.
So you can rename most things:
variables, functions and classes.
In my product class, I've got a variable,
but I'm going to rename.
You select the variable in Visual Studio Code,
right click and then select rename simple.
I'll do this in a moment,
and then you basically give it the new name and press enter.
So if you looked in my original product class,
you'd find that the product
has a property called description.
I'm going to refactor the description property
to be called name instead.
Okay, so a product property description
I decided wasn't a great name for it.
I decided name was a better name.
So let's do that refactoring.
So I'm gonna go into my original folder,
and I'm going to select the description property here.
Right click, and then I'm going to rename symbol.
I'm going to rename it to name.
There we go.
And it changes it everywhere that property occurred
then it's changed it in the code.
Okay, so it changed it here as well.
So that's good.
That was easy enough.
And you can rename, you know, everything.
You can rename variables, classes, functions, and so on.
By the way, in the refactored version,
it contains all of these refactorings,
like in the final version.
So in the refactored folder, it contains all of those,
all of the things we're gonna look at
have already been done in this version.
Okay, so renaming things
is almost like a trivial refactoring, but it is important.
Often when you start coding,
you don't really understand the problem thoroughly.
And after two or three days working with a problem,
you get a better understanding,
or a better name would be X.
So rename it.
Every time you visit the code you could rename things.
You've also got the tests as well, hopefully,
so that you can guarantee, just run the tests again
to make sure that you haven't broken anything
by doing this rename.
Okay, extracting a constant.
Magic numbers in your code are nearly always a bad idea.
I could, again, I could tell you a horror story
about an experience I had when I was about 25
where involving magic numbers in the code.
It was a very painful experience.
I'll never make that mistake again.
There was magic number 42 cropped up in the code,
and the number on its own doesn't have any meaning.
So what you should do is you should,
is you should extract the magic number
into a constant variable, and variables have a name.
And the name is like a comment that describes the intent.
So as an example, we're gonna take a variable.
You right click on the magic number,
and then you extract into a constant.
This is in Visual Studio Code.
Okay, refactor extract the constant.
And then you give the constant a name and press enter.
Okay, so I'll just go back to the previous slide.
There we go.
Let's do that.
If you go into the product class,
here, the tax payable.
I live in the UK, and in the UK sales tax is 20%.
This is meant to calculate the sales tax on a product.
I've got a magic number here.
In the UK, you'd know that 0.2 is the sales tax,
but if you live in the US or in France or Singapore
or some other country you might think
what on earth is this is this number here?
So let's extract that constant.
So we're gonna select an option here and refactor.
Extract to constant.
You can extract it to a constant in the enclosing scope
within the function
or you can extract it into other places.
I'm just gonna extract it into local scope,
and I'm gonna call the new variable sales tax rate.
Okay, so that makes it clearer, doesn't it?
Generally, it's better to have named variables
than to have a comment because comments can lie.
Whereas names of variables are hopefully more stable.
Okay, so I think that's more readable.
What is 0.2?
Oh, it's the sales tax rate.
It's clearer.
Okay, if you have a class, you can extract the constant
as a static class member instead.
Have a look at this.
Imagine that's where I started with.
Instead of declaring a constant inside the function
like I've just done,
maybe that number crops up in several functions.
So you can define it as a class variable instead
to give it a wider scope like this.
Give it a class.
You can basically define a static variable sales tax rate.
And that variable there
basically belongs to the whole product class,
product.sales_tax_rate.
You can then use this variable,
this constant I should say, any way you like in your code.
And I've done that.
If you look at the,
look at the kind of refactored version of my code,
I've defined a static here for sales tax rate.
So I could use it here,
and I could use it in any other methods
where that might crop up.
So I would prefer this approach
because it kind of makes it more visible
that it's a useful constant
that applies for the whole class.
Remember in JavaScript that everything's public.
So if you define this field here,
you can access it in your external code.
You could say product.sales_tax_rate,
and you get the value back, 0.2.
Okay, extracting code into a method.
If you have a method that's getting a bit too long,
you can select some of the code that you want to extract,
and then you can click refactor.
And then in the refactor menu, you can say extract method,
and you can move that code into a separate method.
The motivation is to keep methods short.
Methods should be as short as possible
with one single purpose.
Give the method a name and then press enter.
So let's see an example of that.
Have a look at this code here.
It's a two string function for product.
I'm just gonna go back into the product class
to explain a few things first.
So in my original product class,
a product has ratings.
This here is a variated parameter.
It's an array basically.
When you buy a product a product might have ratings.
Like somebody might give it a five.
Somebody might give it a four.
Somebody might give it a zero.
So it has an array.
That's an array of ratings.
And in my two string function,
it builds up a string that has the name of the product
and the price and a comma separated list of ratings.
So down here, here's my two string function.
It builds up a string of ratings.
First of all, this bit of code here, it has a string.
It says basically, ratings.length.
Imagine there were five ratings.
So it would say five ratings colon.
And then it basically adds each rating in to the string,
actually separated by a space.
Okay, so, and then after that
it'll take the person's, the product's name,
the price of the product in great British pounds,
and the rating string that we just accumulated up here.
So, I mean, it's not that complicated,
but this code here could potentially be getting tricky.
So what I would do is I'd say, right,
that code is doing something distinct.
Why don't I move that code into another method
to simplify this method?
So right click, select the code,
and right click and then select refactor,
and then extract to method in class product.
Okay, you can have inner functions in JavaScript,
one function declared inside another,
but I think having a top level method in product
probably makes more sense.
Okay, so then I give it a name ratings, two string,
something like that.
So the benefits are twofold.
First of all, my original method is simpler and clearer
because it's fewer lines of code.
The complexity of this calculation has been absorbed now
by this function.
So this function can, if the complexity grows,
I can encapsulate that in this function.
The name of that function is effectively a comment.
It describes what the method does.
When I'm reading this code,
I don't really need to care how it's implemented.
I can look at it if I need to, but I don't have to.
I can just say the name of the method tells me what it does.
That's enough.
I don't need to know how.
I just need to know what it does.
It gives you back the ratings as a string,
and then I can use it here.
And in fact, what I could do
is I could probably just get rid of that variable altogether
and then just put that calculation in line here, maybe.
I actually think I preferred it the way it was.
I think that's clearer.
Clarity is the main thing.
So by extracting code into methods,
each method becomes smaller and more focused.
So it's easier to implement correctly.
You can test it in isolation as well.
I could test that method on its own,
and this method becomes simpler.
So it's easier to write, easier to maintain, easier to test.
And the intent is clearer as well.
Okay, so that's basically what I've done.
I called the method format maintenance there.
So the code that was in here,
my two string function now looks simpler.
And this code
which could eventually become quite complicated
has been shoveled off into another function,
like putting stuff in your cupboard under the stairs,
hiding it away.
You don't have to look in there if you don't want to.
Right, avoiding duplication.
Code duplication is not great.
You end up with two lumps of code
that you have to maintain separately.
Have a look.
In my product class, I've got a method,
it displays the count of low ratings.
A low rating is basically a one.
If somebody rates my product as a one,
this function will count how many people
have rated my product as a one.
So it loops through the ratings for the product.
If the rating is a one,
it increments the counter zero initially,
and then it says the number of low ratings was the count.
I also display the number of times
that people gave me a high rating of five.
The code in here is very similar.
Basically the code in there and the code in there
is pretty much identical
apart from the fact that in one case I'm counting ones
and in another case, I'm counting five.
Now, it's not common for code to be that similar
but you know, sometimes you've gotta massage it a bit,
but you will eventually find
that those two functions
are effectively largely duplicating themselves.
Not good.
So what I'm gonna do is
I'm going to rearrange that code slightly
to extract, you know the one or the five into a variable.
Take that one there and basically put it
into a variable here
const targetrating equals one.
I'm gonna do that in my code.
I'm gonna do that twice.
I've got the count of low ratings.
So I'm gonna basically declare,
I'm gonna extract this constant here.
I'm gonna extract that constant.
I wonder if I can actually do that.
Refactor, extract to constant in closing scope
I'm gonna call it target rating.
Okay, and because I know how this demo is going to evolve
in a moment I'm just gonna move that from there up to there.
And I could do the same thing down here as well.
I can extract this number five from there
into a, and refactor and extract to constant
in explosion scope.
I'm gonna call that target rating as well,
targetRating, like that.
And also I'm gonna move that code up here.
So have a look what I've got.
These functions now, and the code in the first function
that code is identical to this code.
That's the same code now, isn't it?
Duplication.
I had to work a little bit to get them identical,
but they are identical now.
So let's extract one of those into a method,
extract to a method in class product.
How about count rating, something like that.
It's intelligent enough to know
that the count rating function needs to have a parameter.
Haven't declared this target rate in here.
It's intelligent enough to know
that it needs to pass that parameter into here.
So it knows what rating it's counting.
It's counting a rating of five in this case.
Now unfortunately, VS Code,
it isn't quite smart enough to realize
that this method was also basically the same.
It didn't realize
that it can also basically do the same function call there
up here as well.
So I'm gonna have to manually do that.
So a little bit of manual effort, but it's not too bad,
not too bad really is it?
So my count low rating,
and my count high rating have been reduced,
and the commonality has been extracted into a shared place.
And I could take it a step further.
I could make this function even more generic
by passing some kind of string into there.
So I'd leave that as an exercise for you to do.
What I will do though is,
is it worthwhile just getting rid of that variable
and just putting the number one here?
Maybe actually.
Sometimes less is more, so I could put five in there,
and then leave it like that.
So I'm not sure whether I'd prefer that
or whether I prefer this.
Having name variables sometimes can make it more explicit.
So, you know, it's a decision to make,
but anyway, it is an improvement, isn't it?
You can see that we definitely have reduced duplication,
and it means I've gotta,
I can test that function once in isolation.
And that's a good thing as well.