Win a copy of Kotlin in Action this week in the Kotlin forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Need help with assignment  RSS feed

 
Mike Terryberry
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello All,

New to BMS AND new to Java programming. I'm taking an introductory course online and one of the assignments is to ask a user for a starting and ending number, print the numbers in ascending order while counting up if the starting number is bigger than the ending number, or print the numbers in descending order while counting down if the ending number's bigger than the starting number. I also opted to do an additional check for invalid input (i.e. a letter entered instead of a number). Here's my code:



OK, so the 'invalid entries' code works, the code for the ValidNumber method works, and when the program counts UP it works. However, if the ending number is bigger than the starting number the program just hangs. My syntax is exactly the same for counting down as it is for counting up so I don't see what the problem is. As an aside, I want to get rid of the duplicate code that checks for invalid entries. I know this would never fly in the real world and is/would be considered poor programming. What I want the program to do is this: After asking for the starting number immediately jump to an external method that checks for invalid entries. When the user enters a valid number have the method return it so it can then be assigned to the start variable. Then, do the same thing for the ending number. I tried before to store the input in a variable and then pass it to the validNumber method as a parameter ("validNumber(number);") but I got this error: "non-static method validNumber(int) cannot be referenced from a static context". I also attempted to parse the input as an integer before passing it as a parameter and that didn't work either. I know there IS a way to pass Scanner input as a parameter. I just can't figure out how to do it. Can anyone tell me how it's done?

 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to the Ranch!

To eliminate duplication, you Extract duplicated code to its own method. If the extracted code is going to be called from a static method like main(), then the extracted method also needs to be static.

See the Java Tutorials on Methods and parameters
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike Terryberry wrote:ask a user for a starting and ending number, print the numbers in ascending order while counting up if the starting number is bigger than the ending number, or print the numbers in descending order while counting down if the ending number's bigger than the starting number.

That seems backwards to me and it actually describes the opposite of part of what you're doing in your program.

Are you sure it's not supposed to be ”print in ascending order, counting up if the starting number is smaller that the ending number and in descending order, counting down if the starting number is bigger than the ending number"? That makes more sense to me.
 
Mike Terryberry
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:
Mike Terryberry wrote:ask a user for a starting and ending number, print the numbers in ascending order while counting up if the starting number is bigger than the ending number, or print the numbers in descending order while counting down if the ending number's bigger than the starting number.

That seems backwards to me and it actually describes the opposite of part of what you're doing in your program.

Are you sure it's not supposed to be ”print in ascending order, counting up if the starting number is smaller that the ending number and in descending order, counting down if the starting number is bigger than the ending number"? That makes more sense to me.


Junilu - you're right. Typo on my part... but you know what I mean.
 
Mike Terryberry
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:Welcome to the Ranch!

To eliminate duplication, you Extract duplicated code to its own method. If the extracted code is going to be called from a static method like main(), then the extracted method also needs to be static.

See the Java Tutorials on Methods and parameters


Junilu, just fyi I'm not familiar with extracting duplicate code. Like I said, I'm a total newb when it comes to Java. For the online course we're using Bluej which is clearly bare bones as far as compilers goes. However, I have been using Netbeans while following an online video tutorial on Java. I'm sure that and/or Eclipse could accomplish that task(?) Regardless of their ability to automate such processes I'd like to be able to figure it out without using a tool (just for knowledge purposes).
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike, don't be intimidated by the fancy name, it's fairly straightforward.

See this example from the Refactoring catalog: http://refactoring.com/catalog/extractMethod.html

Although the example does not show any duplicated code being eliminated, the mechanics are basically the same. You just have the additional step of finding all the other duplicates and turning them into appropriate parameterized method calls. You can do this manually a couple of times but that should get old fast. Having an IDE that can find and replace all other duplicates for you is nice. It's like having one of those robot vacuum cleaners: sure you could do it yourself but why not let the little widget run around the house and do the vacuuming for you instead?
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's a step-by-step example of Extract method to remove duplication. Say you start with this code:

There's obviously a pattern being repeated in this code. Even though there are slight differences, this is still duplicated code. The problem with duplicated code is that the effort to maintain it increases with the number of duplicates. This is not good. If you need to change how a fighter is announced, you'd want to make the change just once. With this duplication, you have to make changes in both sections and make sure they're consistent. It may not be a big deal since this duplication is fairly limited but imagine if you had multiple duplicated sections spread throughout your code.

First, you create a new method and paste a copy one of the duplicated sections into it.

The code in the new method will not compile because you're referencing a parameter that's only in scope in the original method. So you add that parameter to the new method.

Now that it compiles, you rename the parameter so that it makes more sense in the context of the new method.

Next, you deal with the hard-coded "blue corner" and make it a parameter as well. Eliminate any unnecessary comments.

Now you're ready to eliminate the duplication in the original method. Replace each of the two blocks of duplicated code with an appropriate call to the new method that contains the extracted code.

Finally, eliminate any remaining comments that are redundant

Now, if you need to change the way a fighter is announced, you just modify the announceFighter() method and those changes will apply to both fighters. If you actually try to run this code, you might notice that there are still some minor problems with the formatting. In particular, you might want to insert a newline or two somewhere. That's easy enough to do with the refactored code because you just make the changes once.

Notice how nicely the original method reads now? The code in the original method is much shorter and easier to understand. All the details of how a fighter is announced are contained in the announceFighter() method.

You can still make one more refactoring. The names announceFighters() and announceFighter() only differ by one letter and that can be a little confusing. You can rename the original method to something like this:

Now your code tells a clear, well-structured, and non-repetitive story.

That's all there is to it.
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ok, so that wasn't it just yet. To show you how ruthlessly I refactor, I go one more time at it. This time, I read the code OUT LOUD and notice that I'm still repeating myself there. Do that and see if you can tell where there's a bit of a stutter. So I rename a few more things: The naming may look a little unconventional but when you read the code out loud, it really tells a story now. If you fill in a few words here and there, it becomes very conversational: "Announce the bout between the fighter in the red corner and the fighter in the blue corner". It also makes a little smell come out in that the name and the label now seem a little redundant. It's something that you can probably live with for now but imagine someone wants to change this and include information about who is in the corner with each fighter. Let's say now you want to announce the trainer, the cut man, and the promoter for the fighter as well. When you read the code with that kind of naming, it almost leads you to think of changing the class being passed to the announce method as just a Corner object that has properties to encapsulate the corner color, fighter, trainer, cut man, and promoter. So the code naturally becomes something like: Notice that I try to make the names make sense in the context of each level. I often see code that just carries over the name from one level of abstraction to the next but I usually find that changing the name at each level to fit the context makes for clearer code and reveals the focus of that particular level.
 
Campbell Ritchie
Marshal
Posts: 55717
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
How much of that do you have in the toString method of the Fighter class? If that is what the toString method returns, then all you need to do is add the corner colour to it.

I shall leave OP to make some suggestions about how to get the Fighter#toString method to return all that info, or nearly all of it.
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:How much of that do you have in the toString method of the Fighter class? If that is what the toString method returns, then all you need to do is add the corner colour to it.

Interesting question. It certainly is a design option you could choose to try and you could find that it works when you try it. But then again, a new requirement might come along that makes you wonder if that wasn't the best choice after all. I would always drive the choices from a need.

Notice how in the Refactoring before going to the Corner class, all I did was rename. And notice how each refactoring step before was a small change from the previous state. Note the motivation for each small change. It is around clarity of intent, simplicity, or getting the right level of abstraction.

I have said in the past that the Rename, Extract, and Composed method refactoring are the most basic refactoring. Yet I find that these are largely overlooked even by professional developers. In fact most developers I work with have to be constantly reminded, multiple times in just an hour of programming with me, to do one or more of these. These simple refactorings drive you to simplify your code, such that it follows the 4 Rules of Simple Design. I stop when, to borrow from Einstein, I have made the code as simple as it could be, but no simpler.

Why didn't I think of moving those statements into a toString() method? Because I didn't give any example of a requirement that would make it compelling enough for me to try that option.
 
Junilu Lacar
Sheriff
Posts: 11146
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Note that going from this

to this

usually doesn't happen all at once. There's usually a trickle of new requirements coming in that leads to an incremental worsening of the code before it gets better. Entropy in code can be very insidious.

Someone might first come to you and say, "Hey, can you announce who each fighter's promoter is, too?" The easiest and most straightforward thing that most programmers will think to do is to add a promoter field to the Fighter object, so that's what they'll do. Done, everything is fine and dandy.

Until someone again comes along and says "You might as well announce the trainer, too." So they go and add a trainer field and again, everything seems dandy.

Then, "How about adding the cut man, too?" So they go back and add yet another field to Fighter.

Still fine and dandy? Maybe. But now the Fighter object is loaded with all this other information that's arguably not supposed to be in Fighter. If this were a database design where you're thinking about entities and relationships, you might start to see this evolved Fighter object as a "denormalized" entity.

So you might start separating fields out into their own objects and leave references of these things in the Fighter object. And so on and so forth, each change seemingly a small deviation from the last design. It could lead to a more appropriate design eventually but it could also take a wrong turn and lead you to the proverbial corner that you've painted yourself into. The trick is to recognize when you're starting to head into a trap and make the moves to avoid it. The other trick is knowing when is the right time to do anything about anything. I usually wait until I have a new requirement before I start thinking about those things.

I like what Sandi Metz tweeted:
Sandi Metz wrote:Don't write code that guesses the future, arrange code so you can adapt to the future when it comes.

That captures the essence of agility very succinctly.

How did I know to go straight to a Corner object instead of making small increments and possibly painting myself into a corner (pun intended)? I don't know, I didn't really think about it much. Maybe it's kind of like Clint Eastwood's character, William Munny, in the movie "Unforgiven," (excuse the pretentiousness of the self-comparison), in that scene after he kills Little Bill, Gene Hackman's character, and he says "I've always been lucky when it comes to killin' folks." I guess I'm just lucky when it comes to refactoring.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!