Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I would like to ask if it is a good way to have many else if statements based on the boolean conditions like below?
public void borrowItem() throws IOException {
boolean ableToBorrow = isUserAbleToBorrow(cardID);
boolean isDemand = checkDemand(title, authorNumber);
boolean userExists = checkIfUserExists(cardID);
if(ableToBorrow && isDemand && userExists) {
//lots of code....
}else if(!ableToBorrow && isDemand && userExists) {
System.out.println("User limit exceeded");
}else if(!ableToBorrow && !isDemand && userExists) {
System.out.println("User limit exceeded and no book demand");
}else if(ableToBorrow && !isDemand && userExists) {
System.out.println("No book demand");
}else if(ableToBorrow && !isDemand && !userExists) {
System.out.println("No book demand and user does not exists");
}else if(ableToBorrow && isDemand && !userExists) {
System.out.println("Unrecognized user!");
}
}
Is it a good way or there is a better idea in java to do that?
I agree with what GhostCat wrote. It's way too procedural. One way (probably the best way in this case) to implement polymorphism would be the decorator pattern.
Define your interface:
public interface User {
void borrowItem(String item);
String cardId();
}
Create the base implementation:
public final class SimpleUser implements User {
private final String cardId;
public SimpleUser(final String cardId) {
this.cardId = cardId;
}
#Override
public void borrowItem(final String item) {
// Borrowing logic.
}
#Override
public String cardId() {
return cardId;
}
}
And then add decorators for each validation you need. E.g. to check if user exists:
public final class ExistingUser implements User {
private final User origin;
public ExistingUser(final User origin) {
this.origin = origin;
}
#Override
public void borrowItem(final String item) {
if (!exists(cardId())) {
throw new IllegalStateException("Unrecognized user!");
}
origin.borrowItem(item);
}
#Override
public String cardId() {
return origin.cardId();
}
private boolean exists(String cardId) {
// Check if exists...
}
}
And combine them. This way, when you need one additional validation, you add one additional decorator. With ifs, the number of cases would grow geometrically.
new ExistingUser(
new DemandAwareUser(
new SafelyBorrowingUser(
new SimpleUser(cardId)
)
)
).borrowItem(item);
It is very bad style: hard to read and understand, easy to mess up when you are asked to enhance/change behavior. Please note that such code is also extremely hard to test - as you would want to make sure to cover all possible paths that flow can take within such a metho.
The typical answer to such things is to use polymorphism, like having a base class defining some interface, and specific child classes each implementing the interface differently.
In that sense: your code is a clear violation of Tell Don't Ask: you code queries some status from somewhere, to then make decisions on that. Instead, you create classes/objects and tell them to do the right thing (again: that is where polymorphism kicks in).
Nothing wrong with the way it is, but there are other options if you want your code to be more concise. If I change you error messages very slightly, I can write it this way:
if(ableToBorrow && isDemand && userExists) {
//lots of code....
} else {
String errorMsg = "";
if (!ableToBorrow) errorMsg += "User limit exceeded - ";
if (!isDemand) errorMsg += "No book demand - ";
if (!userExists) errorMsg += "Unrecognized user!"
System.out.println(errorMsg);
}
There is also the option of bundling the boolean's into a single integer value. This obfuscates what your code is doing though, and I personally wouldn't use unless you wanted to create an enum to keep track of what the integer values meant;
int status = 4*(ableToBorrow?1:0)+2*(isDemand?1:0)+(userExists?1:0);
switch(status){
case 7: //lots of code....
break;
case 3: System.out.println("User limit exceeded");
break;
//etc...
}
Related
I am newbie to object orientated programming and trying to construct something which resembles a basic vote counter which should take an int parameter that represents a choice of two candidates and print the election results to the terminal window. albeit (the votes attributable to each candidate and the total votes cast)
The method I am looking for should also return a string that gives information on the success or failure of casting the vote.”your vote has been cast” “invalid choice, no vote cast"
I have created a class and the constructors and also implemented some basic get methods.
I am wondering how I should go about achieving this objective albeit through a conditional statement or using some sort of advanced method.
any help in terms of the syntax or wider approach would be appreciated.
public class VoteCounter {
private String candidate1;
private String candidate2;
private int candidate1Votes;
private int candidate2Votes;
private boolean completed;
public VoteCounter(String candidate1, String candidate2) {
this.candidate1 = candidate1;
this.candidate2 = candidate2;
this.candidate1Votes = 0;
this.candidate2Votes = 0;
this.completed = false;
}
public VoteCounter() {
this("CANDIDATE 1", "CANDIDATE 2");
}
public String getCandidate1 () {
return this.candidate1;
}
public String getCandidate2 () {
return this.candidate2;
}
public Boolean getCompleted () {
return this.completed;
}
public void setCompleted (boolean completed) {
this.completed = completed;
}
}
Something like this?
private String vote(int choice)
{
if(choice == 1)
{
candidate1Votes++;
}
else if(choice == 2)
{
candidate2Votes++;
}
else
{
return "invalid choice, no vote cast";
}
return "your vote has been cast";
}
I would do that in more general manner, avoiding code duplication and allowing to change number of candidates easily.
So let's make a class Vote similar to your VoteCounter but only for one candidate, with following fields:
private String candidate; // init this in constructor
private int candidateVotes; // initially 0, so no need to init
and with vote() method like in other answer but also without a candiadate, so:
public void vote() {
candidateVotes++;
}
Then you can make class VoteCounter which will take any number of candidates and will keep them in Array or Map.
Map<Integer, Vote> votes = new HashMap<>();
then you're creating vote method with choice:
public void vote(int choice) {
votes.get(choice).vote();
}
Then all is left is to iterate through your votes map and find the one with biggest number of votes.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I've been trying to get this code to work for the past week now, and every time I make one change I end up with more bugs. Can anyone help figure out where I've gone wrong?
The code is split up into two files: a runner class, and a class with all the methods.
import java.util.Scanner;
import static java.lang.System.*;
public class RPSRunner
{
public static void main(String args[])
{
Scanner keyboard = new Scanner(System.in);
String response = "";
String player = "";
RockPaperScissors game = new RockPaperScissors();
System.out.print("What is your name? : ");
player = keyboard.next();
out.print("type in your prompt [R,P,S] :: ");
response = keyboard.next();
game.setPlayers();
game.convertUserInput(response);
game.setPlayerChoice(response);
game.computerThink();
game.determineWinner();
}
}
The method class:
import java.util.Scanner;
import static java.lang.System.*;
import java.util.Random;
public class RockPaperScissors
{
private String playerName; //used to set player's name
private int playChoice; //player's choice as a number
private int compChoice; //computer's choice as a number
private int playerNumber;
Random rand = new Random(); //allows useage of random methods
public RockPaperScissors()
{
//sets everything to null, prepare for incoming calculations
playerName = "";
}
public RockPaperScissors(String player)
{
playerName = player;
}
public void setPlayers(String player)
{
//good ol mutator method
playerName = player;
}
public String convertUserInput(String response)
{
//Convert R, P, S to integer using switch case
//If invalid input, set to -1
switch(response) {
case "R": playChoice = 0;
break;
case "P": playChoice = 1;
break;
case "S": playChoice = 2;
break;
default: playChoice = -1;
}
}
public boolean setPlayerChoice(String response)
{
//TODO set playChoice to convertUserInput
//return (playChoice != -1)
playChoice = convertUserInput(response);
return(playChoice != -1);
}
public int computerThink()
{
//Use Math.random from 0-2 inclusive
//return it all in one statement so
//return Math.random(whatever);
return rand.nextint(2);
}
public String determineWinner()
{
String winner="";
compChoice = computerThink();
switch(compChoice) {
case 0:
if(playChoice == 1){
winner = playerName;
} else if(playChoice == 2) {
winner = "Computer";
} else if(playChoice == 0) {
winner = "Tie";
}
case 1:
if(playChoice == 1) {
winner = "Tie";
} else if(playChoice == 2) {
winner = playerName;
} else if(playChoice == 0) {
winner = "Computer";
}
case 2:
if(playChoice == 1) {
winner = "Computer";
} else if(playChoice == 2) {
winner = "Tie";
} else if(playChoice == 0){
winner = playerName;
}
} //closes the switch
return winner;
}
}
This is my first major program, so I apologize for any glaring errors or incorrectly-interpreted concepts. I think my major issue lies in the return types, but I'm not positive.
Looking through your code, it is a bit of a mess, so I'll go through step by step.
game.setPlayers();
game.convertUserInput(response);
game.setPlayerChoice(response);
game.computerThink();
game.determineWinner();
You call ALL of these, yet some have return types and are called in previous functions already. For example, convertUserInput.
Your convertUserInput function sets the playChoice variable, declares it returns a String but actually returns nothing. This is called with your clump of functions above, but is then also called by setPlayerChoice, which replaces the playChoice set in the call with, well, nothing. Because nothing is returned you get a compile error.
computerThink returns an int, but you call it above without setting the returned value to anything, then determineWinner is called, which WOULD work had it not been for the above problems.
There is quite a bit wrong with your code. A few obvious ones: you have parameterized your setPlayers method of the RockPaperScissors class to accept a string, but when you invoke it, you dont provide any value, thats a compile time issue. In the RockPaperScissors class you have a method convertUserInput whose method signature says it will return a string, but there are no code paths which return a value in that method. I would do a few more simple tutorials to try to wrap your head around basic OOP concepts then come back to this once you understand basic stuff like, What is an Object? What is a method signature? and most importantly, read and interpret the compile time errors.
It's a little strange that you know your problem lies with the return types but don't know how to fix it. Are you just reading the error message but don't know what its actually saying?
The return type is declared in the first line of a method, the method declaration. The method is expected to return the return type or you will receive a compile-time error.
Some Examples
//these are method declarations
// the return type is before the name of the method
public void setPlayers(String player) {} //return type "void" - this method should not return anything
public String convertUserInput(String response) { // return type "String" - this method NEEDS to return a String
return "A String";
}
Matching method calls
//You need to match the return type with how you call the method
String myPlayers = setPlayers("player"); //WON'T COMPILE - setPlayers returns void, not String
setPlayers("player"); // this is okay, nothing is returned, return type void
String convertedInput = convertUserInput("response"); // this is okay, return type String, which is what convertedInput will be.
convertUserInput("response"); // this is also okay, even though it returns a String we don't have to assign it to a variable. Though in this example calling the method like this is pretty much useless.
int convertedInput = convertUserInput("response"); //WON'T COMPILE - convertUserInput returns String, not an int.
I have one query that is I have used a method but there is many time I have used If Else ..not it become very ambiguous please advise can I use some other conditional loop also..below is my code..
if (cardType == AARP_CARD_TYPE) {
userResponse = messageBox.showMessage("CandidateAARPCardAttachCardToExistingTransaction",
null, IMessageBox.YESNO); // MSG:31.59
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_CANDIDATE_AARP_CARD);
} else if ((cardType == PSC_CARD_TYPE) && ((!PosHelper.isRunningAsService()))) {
userResponse = messageBox.showMessage("PendingPSCCardAttachCardToExistingTransaction", null,
IMessageBox.YESNO); // MSG:31.60
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_PENDING_PSC_CARD);
} else if ((cardType == DR_CARD_TYPE) && ((!PosHelper.isRunningAsService()))) {
userResponse = messageBox.showMessage("PendingDRCardAttachCardToExistingTransaction", null,
IMessageBox.YESNO); // MSG:31.63
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_PENDING_DR_CARD);
} else if ((cardType == WAG_LOYALTY_CARD_TYPE)){
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
WalgreensRewardsConstants.ATTACH_NOT_ON_FILE);
if((!PosHelper.isRunningAsService())) {
userResponse = messageBox.showMessage("CardNotOnFileToAttach", null, IMessageBox.YESNO); // MSG:31.32
// BUC
// 1.22.1
}
} else { // If the device is neither of these, POS displays Message 1
// Button, MSG 31.14. [BUC
// 1.23.2]
displayMessage("InvalidLoyaltyCard");
transaction.setValue(ITransactionHashtableWag.LOYALTY_MESSAGE_DISPLAYED,
NOT_VALID_LOYALTY_CARD);
userResponse = -1;
}
Please advise how can I improve my above logic with some other conditional statements as there is lots n lots of If Else is used..!!
If cardType is an enum, you can add methods to your enum, (say getName, getWag etc.) and call it:
userResponse = messageBox.showMessage(cardType.getMessage(), ...
transaction.setValue(cardType.getWag(), cardType.getRewards());
If it is an int or another non-enum type, you can use a switch as already proposed, or consider switching (haha) to an enum. You could also make PosHelper.isRunningAsService() a boolean parameter to those methods and all your if/else code would be reduced to 3 or 4 lines it seems (although it will introduce some coupling but you seem to have a lot of it already).
Your enum could look like this (simple example that you can complicate as required):
public enum CardType {
AARP_CARD_TYPE {
public String getName() {
return "CandidateAARPCardAttachCardToExistingTransaction";
}
},
PSC_CARD_TYPE {
public String getName() {
return "PendingPSCCardAttachCardToExistingTransaction";
}
};
public abstract String getName();
}
Or more compact, if you don't require complicated logic in the methods:
public static enum CardType {
AARP_CARD_TYPE("CandidateAARPCardAttachCardToExistingTransaction"),
PSC_CARD_TYPE ("PendingPSCCardAttachCardToExistingTransaction");
private final String transactionName;
CardType(String transactionName) {
this.transactionName = transactionName;
}
public String getName() {
return transactionName;
}
}
Use a switch statement instead.
switch (cardType) {
case AARP_CARD_TYPE:
// blah
break;
case PSC_CARD_TYPE:
// blah
break;
// ...
default:
// default blah
break;
}
You have some options: Pattern Strategy, Polymorphism or Events to avoid too much ifs/else
In your example probably the business logic is close to the user interface. You can use the MVC concept to separate the logic from the presentation and reduce the if/elses (if possible).
If you don't like adding methods to CardType as assylias suggested, you can create an 'Action' enum and add the method(s) to that one and use a Map
I'd like to call a method that either returns false, or an integer. At the moment my code is:
int winningID = -1;
if((ID = isThereAWinner()) != -1) {
// use the winner's ID
} else {
// there's no winner, do something else
}
private int isThereAWinner() {
// if a winner is found
return winnersID;
// else
return -1;
}
I don't like the if((ID = isThereAWinner()) != -1) bit as it doesn't read very well, but unlike C you can't represent booleans as integers in Java. Is there a better way to do this?
I would use something similar to Mat's answer:
class Result {
public static Result withWinner(int winner) {
return new Result(winner);
}
public static Result withoutWinner() {
return new Result(NO_WINNER);
}
private static final int NO_WINNER = -1;
private int winnerId;
private Result(int id) {
winnerId = id;
}
private int getWinnerId() {
return winnerId;
}
private boolean hasWinner() {
return winnerId != NO_WINNER;
}
}
This class hides the implementation details of how you actually represent if there were no winner at all.
Then in your winner finding method:
private Result isThereAWinner() {
// if a winner is found
return Result.withWinner(winnersID);
// else
return Result.withoutWinner();
}
And in your calling method:
Result result = isThereAWinner();
if(result.hasWinner()) {
int id = result.getWinnerId();
} else {
// do something else
}
It may seem a little bit too complex, but this approach is more flexible if there would be other result options in the future.
What about something like:
private int getWinnerId() {
// return winner id or -1
}
private boolean isValidId(int id) {
return id != -1; // or whatever
}
int winnerId = getWinnerId();
if (isValidId(winnerId)) {
...
} else {
...
}
This is all quite subjective of course, but you usually expect an isFoo method to provide only a yes/no "answer".
The problem is you are trying to return two values at once. The approach you have taken is the simplest for this. If you want a more OO or design pattern approach I would use a listener pattern.
interface WinnerListener {
void onWinner(Int winnerId);
void noWinner();
}
checkWinner(new WinnerListener() {
// handle either action
});
private void checkWinner(WinnerListener wl) {
// if a winner is found
wl.onWinner(winnersID);
// else
wl.noWinner();
}
This approach works well with complex events like multiple arguments and multiple varied events. e.g. You could have multiple winners, or other types of events.
I'm afraid not. To avoid errors caused by mistaking if(a == b) for if(a = b), Java removes the conversion between boolean type and number types. Maybe you can try exceptions instead, but I think exception is somewhat more troublesome. (My English is not quite good. I wonder if I've made it clear...)
Perhaps you may wish to consider exceptions to help you with your understanding of asthetics of coding.
Use Integer instead of int and return null instead of -1. Look from this point: "I am returning not integer, but some object that represents winner identity. No winner - no instance"
Joe another suggestion, this is constructed based on #Mat and #buc mentioned little while ago, again this is all subjective of course I'm not sure what the rest of your class/logic is. You could introduce an enum with different ResultStatuses if it makes sense within the context of your code/exmaple.
As Matt mentioned you would expect isValid method to return a boolean yes/no (some may also complain of readability)
public enum ResultStatus {
WINNER, OTHER, UNLUCKY
}
This could be an overkill as well and depends on the rest of your logic (and if logic is expanding) but I thought I'll suggest nonetheless my two cents! So therefore in your public class (similar to #bloc suggested) you could have a method such as below that will return the status of the result checked.
public ResultStatus getResultStatus() {
if (isWinner()) {
return ResultStatus.WINNER;
} else {
return isOtherCheck() ? ResultStatus.OTHER : ResultStatus.UNLUCKY;
}
}
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I have the following method:
private static final int OPEN_FOR_RAISE_RANGE = 35;
private static final int OPEN_FOR_CALL_RANGE = 25;
public void foo(double num){
if(num <= OPEN_FOR_RAISE_RANGE){
//do something
}
else if(num <= (OPEN_FOR_RAISE_RANGE + OPEN_FOR_CALL_RANGE)){
//do something else
}
//etc
}
which basically checks what range the number falls in and acts appropriately. I was wondering if there is a nicer/more efficient way to do this other than lots of if's?
Thanks
Check out NumberRange from commons-lang.
NumberRange range = new NumberRange(
OPEN_FOR_RAISE_RANGE,
OPEN_FOR_RAISE_RANGE + OPEN_FOR_CALL_RANGE
);
if(range.containsNumber(num)) {
// do this
} else {
// do something else
}
That's about as good as it gets I think.
Note the compiler will replace the OPEN_FOR_RAISE_RANGE + OPEN_FOR_CALL_RANGE calculation with the value 60 at compile time so you're not computing this on every call.
Seems fine by me, it'd be more worrysome if the if-statements were nested like this:
if (...) {
if (...) ...;
else (...) { ... }
if (...)
if (...)
if (...) ...;
else (...) ...;
}
Then you really should consider breaking the code out to their own methods.
If you had an enormous number of if/ifelse/ifelse/.../else statements, something like this might help:
public interface Choice {
public boolean check(int value);
public void action(int value);
}
public class BelowRange implements Choice {
public static boolean check(int value) {
return (value < 10);
}
public void action(int value) {
// Do something;
}
}
public class Range1 implements Choice {
public boolean check(int value) {
return (value > 10 && value < 50);
}
public void action(int value) {
// Do something;
}
}
...
And in your code:
List<Choice> choices = new ArrayList<Choice>();
choices.add(new BelowRange());
choices.add(new Range1());
...
for (Choice choice : choices) {
if (choice.check(value)) {
choice.action(value);
}
}
I might implement it so the implementations of choice could be static methods, instead of having to instantiate them, but the gist of the thing is there.
If the compiler heavily optimizes this, especially with a static method, it shouldn't be slower at all, and it would make an enormously nested if/else structure a lot more maintainable from a developer's point of view.