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