تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Testing Gutter Game2:00

Every single roll goes to the gutter. It scores a gutter game as 0. Okay, so if I were to create a new game, and, well, we could say roll is 0, so we're charting out the API we want. We have a Game class and a roll method. But how many rolls do you have in a game? Well, again, remember, 10 frames and 2 rolls per frame, which is 20. So let's say we could do a for statement, or let's just do foreach with a range. 1 through 20, we're going to roll a 0. Every single roll of the game is nothing.

Using assertSame2:30

1 through 20, we're going to roll a 0. Every single roll of the game is nothing. Okay, so if we run it, we want it to be—we could do assertEquals, but that will be comparing 0 against null, and that would return true because they're both falsy. So in situations where you want exactly 0 or exactly false or null, do assertSame. So we want the total game score for the User to be 0, and we have our API. So if I give this a run, of course it fails because we have no Game class. There's our next step. I'll create a new class here called Game, and if I switch back, we will import it and give it another shot.

Creating Game API3:06

I'll create a new class here called Game, and if I switch back, we will import it and give it another shot. Okay, now we've changed the error. There is no method called roll. All right, let's create that. Run it again. All right, we've changed the error. There's no method score. Run it again. Okay, so now we expected null to be 0.

Tracking Rolls Array3:46

So if we roll 20 times and each time we only get 1 pin, our total will be 20 times 1, of course. Let's run it, and now that one fails, and it will force us to change our code. So it sounds like we're going to roll a certain number of pins, right, and it sounds like maybe we should track those rolls as an array. We can store that if you want, and then we'll just say, okay, let's record that roll. Finally, well, at the moment, we know this probably isn't right ultimately, but at the moment it seems like we're just getting the total from that array. So let's try to solve it the way we know how at the moment, get the sum of all the values in the array, and if I run it, there we go.

Refactoring to Frames5:46

Of course it fails because we're not accounting for a spare bonus. So yeah, this is the point where we probably need to refactor this. Okay, so let's think about it. I think I'd like to start thinking in terms of frames. We keep saying that word, but it's not represented anywhere here. So to start, we know there are 10 frames per game, right? So let's codify that. And then, to calculate the score, we're going to loop over every single frame. So we'll say foreach, again we'll do range here, foreach frame of the game, let's calculate the score, and then ultimately return it.

So we'll say for each, again we'll do range here, for each frame of the game, let's calculate the score, and then ultimately return it. So the score begins at 0, and then ultimately we return that score. So let's say, well actually, let's do this. This is a failing test no matter what. So while we're doing this little refactor, I'm going to comment that out just so we can make sure the original ones that are passing still pass after this little change. So at the moment, we could just say score plus equals both rolls for the frame, right? So we could say, get the, hmm, we need to track the current roll, don't we? So let's say get the current roll, whatever score you got there, plus the one that came

And immediately we can tell, alright, something's off, because how is this equaling 100? And I think it's because, yeah. So notice we set roll, but at no point do we increment it. So that's what we have to do right down here. What should roll be increased to? I think we add 2, right? So for every frame, we append to the score the first roll and the second roll. So now on the next iteration, we'll do the next two rolls. So if we run that again, there we go. This is a little more what we expect.

Adding Spare Logic7:40

So if we run that again, there we go. This is a little more what we expect. Failed asserting that 18 is 26. So it sounds like right here we need to check for a spare. Notice I'm adding a comment here. I actually think this is a good thing, because when we inevitably refactor this code again, the comment is going to point us to potential method names that should be created or extracted. Okay, so how do we check for a spare? Well, we can look at the current roll, and maybe if that roll plus the one that comes after it, if those two together equal 10, you got a spare.

Adding Strike Logic9:54

Here's the second frame, and we know there's a total of 10 frames. So 10 frames minus 2 frames is 8 frames. 8 frames times 2 rolls per frame is 16. Yeah, it takes a little minute. So we give it a run, and we get undefined offset 19. Hmm, okay. So let's check for a strike. And the same thing, how do we check for a strike? Well, if the currentRoll is equal to 10, you got a strike. So in that case, we would say score plus equals 10, basically, what you got on that currentRoll.

Well, if the currentRoll is equal to 10, you got a strike. So in that case, we would say score plus equals 10, basically, what you got on that currentRoll. And then we get a bonus of the next two, right? So the next roll is a bonus, and then the roll after that is a bonus. Finally, we can increment roll by 1 in this case, because you got a strike on the first try. So let's see how we're doing here. Run the code. It passes.

Extracting Helper Methods13:47

Whenever I change some code, I immediately look for comments, because the comments are an indication that the code below isn't as clear as it should be. So in this case, I wrote, checkForStrike, because without it, it's not instantly clear what we're checking for. So what if we grabbed all this and just extracted it to a method of a similar name? checkForStrike. And now notice the comments in the method are redundant, which means I can remove it. So if we rerun all our tests, it's still passing. Let's do the same thing here. Extract that to a method isSpare.

Let's do the same thing here. Extract that to a method isSpare. Rerun the tests, still passing, and again, notice it's all redundant, so you can remove the comment. Finally, what does this represent? It's basically the default score for the frame. So let's extract a method called, I don't know, DefaultFrameScore or FrameScore, something like that. Let's do those replacements, rerun the code, and we just have a type error that should return an integer.

Let's do those replacements, rerun the code, and we just have a type error that should return an integer. Okay, and now everything is passing once again. And it looks like over here, phpStorm detected, hey, you're doing the same thing, so I can defer to that method as well. That's pretty useful. All right, so let's scroll up. I'll let you take a look at all of this. Now, what else? We could probably remove some of these conditionals.

Simplifying Conditionals15:06

Now, what else? We could probably remove some of these conditionals. So for example, rather than doing else if and else, we can always just say continue. And that way I don't have to add the else there. And then the same thing here. And then we'll do that. Let's see, is that okay? Yes, it is. Now one thing we could do, of course, is inline all of this addition. So we could say currentRole, and then add the next one, and then add the next one.

Now one thing we could do, of course, is inline all of this addition. So we could say currentRole, and then add the next one, and then add the next one. But I kind of like it being clear, but it's not clear that these two represent the bonus for getting a strike. So these two are the strike bonus. But notice I just added another comment, which means it's not clear as it should be. So let's make it clear. Let's add the strike bonus for the currentRole. Now if we come down, we'll have our strike bonus. We accept the role, and that'll return these two.

Let's do the same thing for here. This is the spare bonus. So let's extract that. Spare bonus. There we go. Let's scroll back up. Rerun the code. Ooh, spare bonus must be... we have a type issue. There it is. That should return an integer.

There it is. That should return an integer. Rerun it, and now that's passing as well. Okay, so now notice it's a little more clear. We're going through every frame. Did we get a strike? If so, add that currentRole, which is 10 points, then add the bonus for a strike. Next, did you get a spare? If so, add the default frame score and the bonus. Finally, if you didn't get a strike or a spare, we add the frame score, and then we increment.

Polishing Code Quality17:31

So first up, I'm going to add my doc blocks, and I'll do that behind the scenes. All right, and through the magic of screencasting, that's now done. Looks a little bit better. Next, we're basically going over this class with a fine-tooth comb. We're looking for any odd variable names, methods that don't make sense, little refactors that can be performed. All right, so we roll the ball. That all looks fine. We get the score, and this is a little lengthy. Well, here's some things we could do.

We get the score, and this is a little lengthy. Well, here's some things we could do. We could inline these. So I could say the score is going to be the currentRoll plus the strikeBonus, and that'll still work. Next, the same thing here. So we get the defaultFrameScore plus the spareBonus for the roll, run it, there we go. Next, maybe the sequencing here could be changed. So notice if it's a spare, you get your defaultFrameScore and a bonus, and then we add 2.

And with a little luck, it works. Okay, so maybe that's more clear. Let's think about it. So now we're saying a strikeBonus is the score for the next frame. And that's right sometimes, but it's not necessarily the next frame, it's the next two balls. And in this case, the math works out, but the method name's a little misleading in this case. So these are the sorts of things you have to think about. Even though the math works out, maybe that method call isn't quite right. The bonus is the next two rolls, not necessarily the next frame score.

This is just a simple explanation with no code.

Introducing pinCount Accessor20:53

And that's basically what that is. For a given roll, tell me how many pins were knocked down. So you'll see we get it there, we get it there, we get it here, we get it here, we get it here. It happens over and over. So at some point, that roll's property may become a class, or an object, or a collection. And if that's the case, we may have to update all these different methods accordingly. So if we want to fetch, in this case, the number of pins knocked down for a roll, maybe we can make that a little more clear. Let's try it out.

So we can say pinCount for the given roll. Let's give that a shot. And all that's going to do is just read into that array for the given roll and return the number of pins. But now we're accessing it in one location rather than all these different methods. So let's give it a shot. Everything's working, but now let's switch to it. So give me the pinCount for that roll. And if I run it, it still works. All right.

And if I run it, it still works. All right. Let's keep going. This one as well. Let's do these all at the same time. pinCount for that roll. And that's not right. roll plus one. All right. Run it.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟