Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
Let's take care of a bug we found surrounding uppercase and lowercase guesses.
Learn more
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
So we ran into that slight
little issue that popped up
0:00
when we added an uppercase value as input.
0:03
The uppercase value T is different
than the lowercase value of T.
0:05
So we need to ensure a specific
case in the letters of our data.
0:09
So when dealing with input, what our end
user is providing us through our prompts,
0:13
like those letters, there are a few
things that we can and should always do.
0:16
Firstly, we should check
the correctness of the input.
0:21
This is called validation.
0:23
Input that has been checked
is said to be validated.
0:25
Another option is to modify or
transform the value so
0:28
that it becomes a valid value.
0:32
This process is called normalization,
and the data is said to be normalized.
0:34
So in our, hey, you already guessed
that letter exception example,
0:38
we validated the input and let the caller
know that the validation had failed.
0:42
Now, we're looking at a place where
we need to normalize the input.
0:47
We need to normalize
alphabetic case of our values.
0:50
So, what we'll do is when we start
the game with new Game("Treehouse"),
0:54
we'll ensure that the value is lowercase.
0:58
And then we'll also make sure that any
time someone calls applyGuess on the game
1:00
object like so, we'll take whatever
they gave us and make it lowercase.
1:05
A great practice to make sure things are
working as you anticipate is to write what
1:10
are known as tests.
1:14
Tests exercise your code and
1:15
make sure that your code works
exactly as you expected it to.
1:16
We, unfortunately, are not going to
get into writing tests in this course.
1:20
But we do offer content that covers
testing, check the Teacher's Notes.
1:24
So for now, while we're coding up this
validation and normalization a bit,
1:28
we'll do some quick manual
spot check testing.
1:32
I'd like you to get a feel for what kind
of tests we might actually write, and
1:35
show you why they're such a critical
part of writing applications.
1:39
Let's get to it.
1:42
Okay, so let's get to work on this bug.
1:44
I'm gonna pull this over
into In Progress column.
1:46
And first off,
1:50
let's make sure that the answer
that's passed in is always lowercase.
1:51
And we can do that by this,
toLowerCase, right?
1:55
Remember that on strings toLowercase.
1:59
Now to tackle the guest side of things,
2:01
one thing that we haven't even checked is
if our user is even giving us a letter.
2:04
So let's go explore the character class,
the documentation for the character class.
2:09
So if we go here and we go Java 8,
and we search for Character.
2:13
So this first result here.
2:19
So the character class is a wrapper
class for our primitive data type char.
2:21
Now much like how we saw integer
wrapper for a class for ints, right,
2:26
where we used integer.parseInt.
2:30
Well since a primitive char
doesn't have any methods,
2:32
this class is used to provide
helper methods for chars.
2:35
And it does it through, guess what.,
let's go look at the methods here,
2:38
through static methods.
2:43
So, here's some common getters and things.
2:45
But what we're looking for
is we're looking for something that says,
2:47
is this a letter?
2:51
So, if we look through here and
we say, is letter.
2:52
Hey, look at that, it takes a char and
it returns a boolean of true or false.
2:55
So, let's try it out.
3:00
Let's pop over to jshell.
3:01
So, we come in here and we'll say,
Character.isLetter, and
3:02
I sure hope that a is.
3:07
And boom, it returns true,
cool, just like we thought.
3:09
So what happens about the character 2 or
a weird @ or something?
3:12
Okay, I'll submit it works for
uppercase too right?
3:17
Great, so I wonder if there's
a toLowerCase for chars,
3:21
just like we looked at for
the strings, I wonder if that exists.
3:24
So let's come here and
I'm just going to do a search.
3:28
And a look for to, look at that,
there it is, toLowerCase.
3:30
And it's a static method
off of the char and
3:33
it converts the character
argument to lowercase, cool.
3:35
So we could also for over here we
could say a character too low and
3:38
just tap complete and
boom there it is to lower case and
3:42
if we give it an a That should
return as a lowercase A, beautiful.
3:46
Okay, so with that information, we should
be able to get a valid lowercase letter
3:51
and throw an exception
if in fact we don't.
3:56
You know what?
Now is a great time to show off a concept
3:59
that we haven't touched on yet,
and that is private methods.
4:01
So our game object here currently is doing
some validation inside of this applyGuess.
4:05
Let's go ahead and
let's make a new method and
4:11
we'll kinda group our
logic together there.
4:13
So we'll make it private.
4:16
And by making this private, it will not be
accessible to anyone but the class itself.
4:18
So we'll say private is gonna return
a chart that has been normalized.
4:23
All right, so let's say normalizeGuess.
4:28
And it's gonna take a char and
it's gonna return a char.
4:33
So again, because it's private, the
prompter, like for instance, the prompter
4:37
can't say game.normalizeguess,
only our code here can.
4:41
So this method is a good way to
group common functionality and
4:45
not clutter up your other methods.
4:49
Here let's build it.
4:51
So first we wanna know if
it is not a letter, right?
4:53
So if it is not a letter,
gonna pass on letter.
4:58
Ideally this would kinda be
a custom exception, but for
5:05
now we'll just do a throw new
IllegalArgumentException, right?
5:09
They passed in a illegal
argument to make a guess.
5:13
So we'll say a letter is required.
5:16
So now that we know it's a letter,
we can transform it to lowercase.
5:22
So we can say letter =, again using
that same character wrapper class,
5:25
toLowerCase(letter).
5:30
Awesome, great, now we already have
some validation and applyGuess,
5:35
why don't we put that in
here in the normalizeGuess.
5:39
So I'm going to cut that out,
and I'm gonna paste it here.
5:42
And then finally our normalizeGuess
needs to return the letter, great.
5:46
So now in applyGuess we can just
call our private method there.
5:52
We'll say letter = normalizeGuess(letter).
5:56
See how it keeps things readable?
6:02
We could very easily just jam all of these
lines in this applyGuess method here.
6:04
But see how nice and concise this is?
6:08
Methods provide a great
way to name grouped logic.
6:10
So how about we do this, let's add
a little smarts to our prompter object.
6:14
If we come over to prompter object and
we look here at promptForGuess.
6:18
What if we made this thing just keep
trying until it got a valid guess?
6:21
Sounds good, right?
6:26
So there's a couple ways
to do this approach.
6:27
But first let's move this isHit up the top
here, let's get this stuff down here.
6:29
So we move isHit to the top.
6:36
And I'm gonna bring this scanner
definition to the top too.
6:38
Let's go ahead and do that.
6:42
It's kinda common to the whole method,
so we'll bring him to the top.
6:43
We'll set up a new variable there.
6:47
And let's do a trick here.
6:49
Let's store a new value that we're gonna
use to keep track of state in this method.
6:51
Did we get an acceptable value?
6:58
So we're gonna set that
to default to start with.
6:59
Okay, so like we said,
we wanna do a loop here.
7:02
We wanna loop through things, and
we wanna make sure that it happens once.
7:05
So while we don't have
an acceptable guess, but
7:09
we wanna make sure it happens once,
so that's a do while loop.
7:12
So again, that starts like this,
you say do.
7:15
And then we kinda wanna run all this
stuff, don't we, to right about here.
7:18
And then we're gonna close that do loop,
see how it's highlighted?
7:22
And we'll say while,
we'll go not isAcceptable.
7:25
Speaking of not acceptable,
look at this lineup of this code.
7:31
Let's go ahead, and
I'm gonna highlight this.
7:35
And I'm gonna do a command bracket or
a control bracket, right bracket.
7:38
And it will move,
you can move left or right with that.
7:45
There we go.
7:49
So what we wanna do is we wanna
make sure that we flip or
7:51
change the isAcceptable
value to true here.
7:56
And what's happening is
if it comes through here,
8:02
we know that it's valid,
otherwise it's coming here.
8:04
And now, since we're looping,
we can try to give a cleaner message.
8:07
So let's switch this to a printf.
8:10
And we'll put a format string here of,
we'll say, here's your error message.
8:12
Please try again.
8:16
The famous Please try again.
8:19
All right, so we're gonna pump in there,
just like we were before,
8:21
the exception message that we set, okay.
8:26
And we're still returning the isHit.
8:31
So this is gonna loop and
will return isHit, okay.
8:33
Let's mentally walk this really quick.
8:36
So this method, promptForGuess is called.
8:39
And we set up a variable
to know if it's a hit and
8:43
we set a variable to
know if it's acceptable.
8:45
Our first iteration through the loop,
8:49
which will always happen because
we're using a do while loop.
8:51
It's going to get an input.
8:55
It's gonna pull off that input.
8:58
And because our input returns a string,
we have to use this charAt.
9:01
Sure would be nice if applyGuess took
a string for us, but it doesn't.
9:07
So anyway, applyGuess takes the guess,
and in here, it does some normalization.
9:10
So we pass that single charge through and
it has a way of normalizing our char.
9:17
Let's take a look at that one more time.
9:21
As we come through applyGuess,
it comes in here,
9:23
it calls this private
method normalizeGuess.
9:25
And we pass that original guess through,
9:28
it comes through here it does
all of our checks that we want.
9:30
If it makes it all the way through here,
we have a valid letter.
9:33
If not,
it throws an IllegalArgumentException.
9:36
Now because one of those exceptions will
break out of this try block, it will run
9:39
this message, and this isAcceptable
will never be flipped to true.
9:43
So therefore, when it comes back down
here to check, it will go again, and
9:47
again, and again.
9:52
So let's walk that really quick with one.
9:54
So if the person entered in a 1 here,
and they came and guessed, and
9:56
the guesser says, trying to be tricky for
some reason, put in a 1.
9:59
They hit applyGuess,
they come on over to game.
10:03
It would go to normalizeGuess,
isLetter, false, a letter is required.
10:05
Here it comes, a letter is required,
please try again.
10:08
And notice that it jumped
over the isAcceptable.
10:12
So isAcceptable is still false.
10:15
So take a second and think about an app
that you've used that takes your input.
10:18
Now this loop is pretty common right?
10:22
Whether it be on the web or
on an app on your phone or tablet.
10:24
Do you follow it?
10:27
It is totally understandable for
you not to grasp this immediately.
10:29
This very well may be the first
time that you're thinking about
10:32
this side of the application.
10:35
But I know that you've
filled out web forms before.
10:37
So you've had this experience
that we're creating.
10:39
So if you need to, go ahead and pause me,
and try to walk through each line there
10:41
on your own in your head, or
out loud if you aren't in public.
10:45
Rewind me a bit if you wanna
hear me explain it all again.
10:48
Okay so, let's give this a test run.
10:53
I am going to do a clear && javac
Hangman.java && jave Hangman.
10:56
So let's make sure that we
can't guess a number, right?
11:10
That was our first exception.
11:12
A letter is required, beautiful.
11:14
Nice, and there it is,
it'll keep going and keep on asking me,
11:16
it's pretty insistent there.
11:19
So that works.
11:20
So what happens if I give it a capital H?
11:21
Awesome, it lowercases it.
11:24
You know what, actually we moved the code
that tests the checks of duplicate
11:26
guesses into that normalizeGuess method.
11:30
So we'd better test that too,
to make sure it's working.
11:32
So I'm gonna guess an h again.
11:35
Cool, it says h has already been guessed.
11:36
And then I'm gonna guess t,
and then Guess it again.
11:39
Cool, and we're still at seven tries.
11:41
Nice, we did it.
11:44
Okay, great, and I just wanna point out
if we had actually written a test for
11:45
all that,
we'd know that we didn't break it.
11:50
Because we could just
run a series of tests.
11:52
But as it stands, we have to test that
every single time we change our code.
11:54
We would write tests for one, and
we'd write tests for uppercase, and
11:57
we'd make sure that that worked.
12:00
In fact, we'd also test what happens if
somebody just came here and pressed Enter.
12:02
No, look, it's trying to get the first
12:07
character out of a string that
doesn't have any characters.
12:10
Well that code was the code we
were wanting to clean up anyway.
12:14
So let's go ahead and
let's close that issue.
12:16
And unfortunately,
lets make a new one that says,
12:20
BUG: Sending no values on
a guess causes a crash.
12:26
There we go.
12:33
And let's label that,
let's give that a red label.
12:36
All right, here we go.
12:39
Nice job on the looping and
exception handling.
12:42
Doing that manual testing or QA,
which stands for quality assurance,
12:44
helped us to uncover another issue that
we should probably fix here shortly.
12:48
If you don't enter a letter, the
application encounters a critical error.
12:52
Are you beginning to see how handy
it would be to be able to run
12:57
a bunch of tests and make sure
everything is working at all times,
13:00
especially if you change a bit of code?
13:03
You would just run the test to
make sure everything still works.
13:05
As it stands, we have to do that ourselves
every single time the code changes.
13:08
All right, let's refactor in clean up
that code right after this exercise.
13:13
You need to sign up for Treehouse in order to download course files.
Sign upYou need to sign up for Treehouse in order to set up Workspace
Sign up