Getting rid of too many if's. What's the better approach? - java

My issue is related to design currently. I am having a jsf parameter page and it submits
different parameters to generate a jasper report. E.g. Nationality, Travel type, Visa type,
Gender and so on. Parameters can be a combination. E.g. At one time user can select
nationality and visatype and leave others blank which will make other's default to the value
of ALL. I submit the id's from the parameter page. If nothing was selected by the user i am
setting the values manually to ALL in the setter methods. Here is a snapshot of my managed
bean method and the POJO.
private ReportBean generateReportBean(TravelDetailSearchParams searchParams, String reportPath){
TravelDetailReportBean travelDetailReportBean = new TravelDetailReportBean();
if(searchParams.getGender().getId() != 0){
for(Lookup lookup : gender){
if(lookup.getId() == searchParams.getGender().getId()){
travelDetailReportBean.setGender(lookup.getDescEnglish());
break ;
}
}
}
else{
travelDetailReportBean.setGender(searchParams.getGender().getDescEnglish());
}
if(searchParams.getTravelType().getId() != 0){
for(Lookup lookup : travelType){
if(lookup.getId() == searchParams.getTravelType().getId()){
travelDetailReportBean.setTravelType(lookup.getDescEnglish());
break ;
}
}
}
else{
travelDetailReportBean.setTravelType(searchParams.getTravelType().getDescEnglish());
}
if(searchParams.getPort().getId() != 0){
for(Lookup lookup : port){
if(lookup.getId() == searchParams.getPort().getId()){
travelDetailReportBean.setPort(lookup.getDescEnglish());
break ;
}
}
}
else{
travelDetailReportBean.setPort(searchParams.getPort().getDescEnglish());
}
if(searchParams.getNationality().getId() != 0){
for(Lookup lookup : country){
if(lookup.getId() == searchParams.getNationality().getId()){
travelDetailReportBean.setCountry(lookup.getDescEnglish());
break ;
}
}
}
else{
travelDetailReportBean.setCountry(searchParams.getNationality().getDescEnglish());
}
if(searchParams.getVisaType().getId() != 0){
for(Lookup lookup : visaType){
if(lookup.getId() == searchParams.getVisaType().getId()){
travelDetailReportBean.setVisaType(lookup.getDescEnglish());
break ;
}
}
}
else{
travelDetailReportBean.setVisaType(searchParams.getVisaType().getDescEnglish());
}
logger.debug("nationality: " + travelDetailReportBean.getCountry());
logger.debug("travelType: " + travelDetailReportBean.getTravelType());
logger.debug("visatype: " + travelDetailReportBean.getVisaType());
logger.debug("port: " + travelDetailReportBean.getPort());
travelDetailReportBean.setReportName(BorderEntryExitConstants.TRAVEL_DETAIL_REPORT_NAME);
travelDetailReportBean.setReportPath(reportPath);
return travelDetailReportBean ;
}
The POJO code is shown below
public class TravelDetailReportBean extends ConcreteReportBean {
private String gender ;
private String travelType ;
private String port ;
private String country ;
private String visaType;
public String getGender() {
return gender;
}
public void setGender(String gender) {
if(gender == null || gender.equals("")){
this.gender="ALL";
}
else{
this.gender = gender;
}
}
public String getTravelType() {
return travelType;
}
public void setTravelType(String travelType) {
if(travelType == null || travelType.equals("")){
this.travelType ="ALL";
}
else{
this.travelType = travelType;
}
}
public String getPort() {
return port;
}
public void setPort(String port) {
if(port == null || port.equals("")){
this.port ="ALL";
}
else{
this.port = port;
}
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
if(country == null || country.equals("")){
this.country ="ALL";
}
else{
this.country = country;
}
}
public String getVisaType() {
return visaType;
}
public void setVisaType(String visaType) {
if(visaType == null || visaType.equals("")){
this.visaType ="ALL";
}
else{
this.visaType = visaType;
}
}
}
Issue is the generateReportBean method. i am putting to many if's to see if the id is not
zero get the description of that id from lookup else just set it like that and inside bean
setter i am checking for null. If null setting it to ALL.
My issue is currently i have few parameters and these if's can work for a while but what
if the search parameter grow. THe if's will look ugly. Can someone suggest me a better approach
to get rid of these if's.
Thanks,
Peter

If Lookup is List of objects then you can provide equals method based on id and then you can directly do
if(lookup.contains(searchParams.getTravelType()))
{
//code here
}

Follow Chain of Responsibility design pattern.
This will increase the code readability and maintainability
Reduces the number of "if" loops
Ref: http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

Divide the parsing pieces into different functions. eg.
checkgender(searchParams.getGender(), travelDetailReportBean);
or
travelDetailReportBean.setGender(checkgender(searchParams.getGender()));
Depends on how you want it

Yes you can put these methods into a utility class and do as Minion has suggested.
travelDetailReportBean.setGender(ParamUtil.checkGender(searchParams.getGender());
Putting validation condition in a dto as you have done is not a smart coding move. Is it a generic
parameter screen you are trying to make? Mostly, every report should have a separate parameter
screen if every report parameters differ.
HTH,
Ben

IMHO testing for empty/null Strings is too common to code it manually. I like to use StringUtils from Apache Commons lang to simplify it. Combined with Java's ? operator, you can write it much clearer:
private static final String OPTION_ALL = "ALL";
public void setGender(String gender) {
this.gender = StringUtils.isEmpty(gender) ? OPTION_ALL : gender;
}
Of course, if you have a large number of fields a generic approach may be justified.
EDIT: Conceptual consideration
As I see, you perform value checking in the classes setter methods. I would advise against doing so: I expect setters to only assign a value to an instance variable. If they do more, this might lead to confusion. You are probably using the class to parametrize some search function, so a better place for ensuring non-empty member values would be right before performing that search.

You may also annotate the fields that you need to do a lookup and then use a single loop to see their values in searchParams and if id is present, do a lookup.

Related

How to avoid many if-else statements

I have a class like this..
Class A {
public void someNullCheckingMethod(Student stu) {
if (stu.getName() != null) {
String name = stu.getName();
} else {
// get name from other source(its something like
// fallback)
}
if (stu.getId() != null) {
String id = stu.getId();
} else {
// get id from other source(its something like
// fallback)
}
if (stu.getAddress() != null) {
String address = stu.getAddress();
} else {
// get address from other source(its something like
// fallback)
}
if (stu.getCity() != null) {
String city = stu.getCity();
} else {
// get city from other source(its something like
// fallback)
}
if (stu.getGender() != null) {
String gender = stu.getGender();
} else {
// get gender from other source(its something like
// fallback))
}
}
}
is there a way to avoid too many if statements? As you can see here I am checking null condition for each property but i don't want many checks to get desired result just want to reduce if conditions as well as want to get same desired result whatever I will get by putting all these if conditions.
Since you don't provide any context there's a few things you can do based on some assumptions.
Assumption one:
if the values are initialized as null
String name;
// or
String name = null;
Then you can just assign the values and ignore if the fields are null or not since the class members are null already.
String name = stu.getName(); // String name = "Some Name" OR null, depending on return value of method
Assumption two:
If you just want to avoid the if statements you can go with ternary operators
String name = stu.getName() != null ? stu.getName() : null; // Or some other default value
There are a few other methods that pops into my mind as well but without more context they are a bit useless at this point.
You could at least reduce the "verbosity" with Optional.
String name;
if (stu.getName() != null) {
name = stu.getName();
} else {
name = "default"
}
Would become
String name = Optional.ofNullable(stu.getName()).orElse("default");
Th choice is yours to return an Optional directly from the POJO Student for any value that could be null.
This would give a cleaner solution :
String name = stu.getName().orElse("default");
If getName looks like :
public Optional<String> getName(){
return Optional.ofNullable(name);
}
If using an external library is an option, then you should take a look at Dozer or MapStruct.

Create a Method to set data to the object

Writing a code for checking data on different conditions.
I have an ArrayList of CRM objects "actionsList" (made in another class).
Here I check these objects for different conditions.
Objects which satisfy the conditions I have to add to the ArrayList "remarksList".
The question is how to create a method setRemarkObject() to set all data to remarkObject at once?
Not to write each time:
remarkObject.setRemark(checkString);
remarkObject.setNumber(crm.getNumber());
remarkObject.setDealer(crm.getDealer());
remarkObject.setName(crm.getName());
...
is it correct now?
import java.util.ArrayList;
public class Conditions {
static ArrayList<CRM> remarksList = new ArrayList<CRM>();
public ArrayList<CRM> conditionsChecking() {
for (CRM crm : App.actionsList) {
CRM remarkObject = new CRM();
remarkObject.setNumber(crm.getNumber());
remarkObject.setDealer(crm.getDealer());
remarkObject.setName(crm.getName());
remarkObject.setGroup(crm.getGroup());
remarkObject.setClientStatus(crm.getClientStatus());
remarkObject.setEntity(crm.getEntity());
remarkObject.setTypeOfContact(crm.getTypeOfContact());
remarkObject.setTypeOfFirstContact(crm.getTypeOfFirstContact());
remarkObject.setSourceOfFirstContact(crm
.getSourceOfFirstContact());
remarkObject.setOfferType(crm.getOfferType());
remarkObject.setEventDate(crm.getEventDate());
remarkObject.setBrand(crm.getBrand());
remarkObject.setCarClass(crm.getCarClass());
remarkObject.setModel(crm.getModel());
remarkObject.setCarCode(crm.getCarCode());
remarkObject.setWeek(crm.getWeek());
remarkObject.setMonth(crm.getMonth());
remarkObject.setYear(crm.getYear());
remarkObject.setAmmount(crm.getAmmount());
remarkObject.setSalesman(crm.getSalesman());
remarkObject.setPhone(crm.getPhone());
remarkObject.setEmail(crm.getEmail());
remarkObject.setAddress(crm.getAdress());
remarkObject.setCreationDate(crm.getCreationDate());
remarkObject.setCreationTime(crm.getCreationTime());
remarkObject.setModificationDate(crm.getModificationDate());
remarkObject.setModificationTime(crm.getModificationTime());
remarkObject.setBackdating(crm.getBackdating());
if ((crm.getClientStatus().equals("Yes")) && ((crm.getAdress().isEmpty()))){
crm.setRemark("Client's address is empty");
remarksList.add(remarkObject);
}
else if ((crm.getClientStatus().equals("Yes")) && (crm.getPhone().isEmpty())){
crm.setRemark( "Phone field is empty");
remarksList.add(remarkObject);
}
///....
else
crm.setRemark("Nothing wrong");
/// not adding to remarksLis
}
return remarksList;
}
}
You seem to be doing the same thing over and over again, with the addition of a String which states if a field is left empty. So Why not move the setters one block up, outside of the for loop?
static ArrayList<CRM> remarksList = new ArrayList<CRM>();
public ArrayList<CRM> conditionsChecking() {
for (CRM crm : App.actionsList) {
CRM remarkObject = new CRM();
remarkObject.setNumber(crm.getNumber());
remarkObject.setDealer(crm.getDealer());
remarkObject.setName(crm.getName());
remarkObject.setGroup(crm.getGroup());
//etcetera, etcetera, all setters, except remark
if ((crm.getClientStatus().equals("Yes")) && ((crm.getAdress().isEmpty())))
crm.setRemark("Client's address is empty");
else if ((crm.getClientStatus().equals("Yes")) && (crm.getPhone().isEmpty()))
crm.setRemark( "Phone field is empty");
// etc, etc
else
remarksList.add(remarkObject);
}
return remarksList;
}
For setters and getters in general, you can also use a constructor:
public CRM(String number, String dealer, String name, ...) {
this.number = number;
//...etc
}
Or a builder
public CRM() {}
public CRM setNumber(String number) {
this.number = number;
return this;
}
public CRM setDealer(String dealer) {
this.dealer = dealer;
return this;
}
//and use it like this
CRM crm = new CRM().setNumber("123").setDealer("dealer"); //et cetera
From what I understand you are copying the crm object into the remarkObject in every if-else statements. Why not copy it before all the if statements and only set the remark(checkString) inside the if-else statements? Then you can add it to remarkList after all of those statements.
You can copy the object using CRM constructor ie. CRM(CRM crm) or by cloning the object:
How do I copy an object in Java?
Also you are checking crm.getClientStatus().equals("Yes") in every of your if statement - why not check it once at a begining?

How to compare value of array of objects?

There is a collection of 20 objects of a POJO class. I Want to write a method that return objects with distinct value. Now this is my Pogo class
class Student {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName( String firstName ) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName( String lastName ) {
this.lastName = lastName;
}
}
Now i want some method which returns unique last names values. I could not understand which logic i have to put in this.
If you are using something like Eclipse, you can right-click the source and select Source > "Generate hashCode() and equals()...". Doing so will yield something like this:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
Then, you'll want to add your objects to an instance of Set, maybe HashSet. Sounds like you can just return the populated Set then.
See also this.
EDIT: Note that I am not suggesting to put all of this on the Student class. The code shown above goes on Student, but the method that returns the set of distinct students goes somewhere else.
EDIT 2: If you are only interested in unique last names, you could modify hashCode() and equals() to not consider first name, but I concede that this would be quite unintuitive and recommend to avoid this in any circumstance other than an academic exercise. So, more correct might be to layer on an instance of Comparator that only considers last name--see doc and this.
You can use an Arraylist, it has a built in function called .contains() which checks if the arrayList contains a specific value. So you would create an arrayList of last names and if it doesn't exist in the array list, just add it. See http://docs.oracle.com/javase/1.5.0/docs/api/java/util/ArrayList.html#contains(java.lang.Object)
You can try to use Set, if you need to get only one field, or Map, if you need to know object(student) with this field.
If you need to know all distinct Students (pair: first name + surname), you need to override getHashCode() and equals methods and use HashSet, HashMap
An easy way (for a beginner) to do this is just create a new array (same size of the input array). Then to loop through your array then compare every value to every other value in the array. If you can't find a match, then put this value in the new array.
Pseudo code:
public static Student[] GetUniqueLastNames(Student[] students){
Student[] retArray;//new array
for(i = 0; i < students.size; i++){
unique = true
for(j=0; j < students.size; j++){
if(i != j){//make sure its not comparing the same value
if(students[i].lastname.equals(students[j].lastname)){
unique = false
break
}
}
}
if(unique){
retArray[i] = students[i]
}
}
return retArray
}
Note: There are far better ways of doing this, but this is a nice basic way to do it if you're learning Java (or programming in general).
If you don't care about keeping the order of the objects, you can use a set:
public static <S extends Student> Collection<S> uniqByLastName(Collection<S> source){
TreeSet<S> result = new TreeSet<S>(new Comparator<S>() {
#Override
public int compare(S s1, S s2) {
return s1.getLastName().compareTo(s2.getLastName());
}
});
result.addAll(source);
return result;
}
If you care about the order
public static <S extends Student> Collection<S> uniqByLastName(Collection<S> source){
Collection<S> result = new ArrayList<S>();
Set<String> addedStudents = new HashSet<String>();
for(S student : source){
String lastName = student.getLastName();
if(!addedStudents.contains(lastName)){
result.add(student);
addedStudents.add(lastName);
}
}
return result;
}
If you want to modify the collection without returning a new one
public static <S extends Student> void uniqByLastName(Collection<S> source){
Set<String> addedStudents = new HashSet<String>();
Iterator<S> iterator = source.iterator();
while(iterator.hasNext()){
S student = iterator.next();
String lastName = student.getLastName();
if(addedStudents.contains(lastName)){
iterator.remove();
} else {
addedStudents.add(lastName);
}
}
}
If you are using Java 8, you can use lambda expression to solve it. Using following code snippet should solve your problem:
list.stream().collect(Collectors.toMap(Student::getLastName, p -> p, (p, q) -> p)).values();
Note: it will return first student with a given last name and as you might have already guessed, you don't need to override equals and hashcode.

Excessive use of If else statements

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

Java - Better idiom for my control flow

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;
}
}

Categories

Resources