What approach/method to use instead of multiple if statements - java

I have a java web app that asks the user to select between 4 options. They can choose 1, all, or any combination of the 5 options. Their choices are read into a Hashmap with true/false values. If the option is selected it's true, unselected is false. Depending on what the user chooses, a different file is selected from the resources folder to be processed. My problem is that the code is just a complete mess of logic and I'm sure that there's an easier way to implement it. The following is dummy code for my problem.
public class offerSelector {
public void selectOffer(Map params) {
/* Map params = Map<String, String> params = new HashMap <>();
It contains values ("internet","true),("phone","true"),("tv","true"),("cell","true")
*/
boolean option_1 = params.get("internet");
boolean option_2 = params.get("phone");
boolean option_3 = params.get("tv");
boolean option_4 = params.get("cell");
File offer = null;
if (option_1 == true && option_2 == false && option_3 == false && option_4 == false) {
offer = new File("internet_order");
}
else if(option_1 == false && option_2 == true && option_3 == false && option_4 == false) {
offer = new File("phone_order");
}
//continues like so with all possible combinations
else if(option_1 == true && option_2 == true && option_3 == true && option_4 == true) {
offer = new File("all_elements_order");
}
processOrder(offer);
}
}

I am a big fan Of pushing this parsing into a custom Object like
public class SomeObject
{
public SomeObject(Values)
{
this.options1 = //Something
this.options2 = //Something
this.options3 = //Something
this.options4 = //Something
}
public boolean isPhone() {return option1 && option2 && option3 && !option4;}
}
Then when you use the option you can do:
var x = new SomeObject(Values);
if (x.isPhone) {
// DO IS PHONE Branch
}
if (x.isFax) {
// DO IS Fax Branch
}
This is better because the parsing logic is excluded to a single class with a single responsibility. and then its clear in your if block what you are looking at.
The other options is to return an Enum from you SomeObject class and use a true case/switch statement.

There are a lot of solutions. For example the following.
Define interface Action:
interface Action {
boolean apply(Map<String, String> params);
void perform(Map<String, String> params);
}
Define enum Actions:
enum Actions implement Action {
ONE {
boolean apply(Map<String, String> params) {/*implement it*/}
void perform(Map<String, String> params) {/*implement it*/}
},
TWO {
boolean apply(Map<String, String> params) {/*implement it*/}
void perform(Map<String, String> params) {/*implement it*/}
},
;
//etc.
}
Implement your logic inside the call back methods. Obviously give the enum constants normal names.
Now your code can look like:
public void selectOffer(Map params) {
for (Actions a : Actions.values()) {
if (a.apply(params)) {
return a.perform(params);
}
}
}

You could try and use a custom object, let's say Options:
//note that a lot of common stuff like constructors or modifiers are stripped for simplicity
class Options {
boolean internet;
boolean phone;
...
public void equals( Object other) {
return other != null &&
other.getClass().equals( getClass()) &&
other.internet == this.internet &&
other.phone == this.phone &&
...
}
public int hashCode() {
//left for your excerise, should match equals
}
}
Map<Options, File> files = ...; //create and fill
Then parse the boolean parameters and create an Options instance which you use to look up the file in the map, e.g.:
Options paramOptions = new Options(/*booleans parsed from params*/);
offer = files.get( paramOptions );

Use a binary notation. each bit represents one option:
option4 is true, option3 is true, option2 is false and option1 is false will be 1100
1100 bin = 12 dec.
Each combination stands for a decimal number, which you can use in a switch statement.
I hope you understand what I mean.

Encapsulate it all away. Hide the details of sifting through the permutations of options in the guts of another class. Something like this...
//controller code
Boolean internet= params.get("internet");
Boolean phone = params.get("phone");
Boolean tv= params.get("tv");
Boolean cell = params.get("cell");
File offer = FileHelper(internet, phone, tv, cell);
//end controller code ...
public class FileHelper {
private final String PHONE = "phone_order";
private final String INTERNET= "internet_order";
private final String CELL= "cell_order";
private final String TV = "tv_order";
private final String ALL = "all_elements_order";
private boolean[] options;
public FileHelper(Boolean phone, Boolean internet, Boolean cell, Boolean tv) {
options = new boolean[4];
options[0] = phone == null ? false : phone;
options[1] = internet == null ? false : internet ;
options[2] = cell== null ? false : cell;
options[3] = tv == null ? false : tv ;
}
public File getOffer() {
File f;
if ( includeAll()) f = new File(ALL);
if ( phoneOffer()) f = new File(PHONE);
if ( internetOffer()) f = new File(INTERNET);
// .... and so on
return f;
}
private boolean includeAll() {
for(boolean b : options) {
if (!b) return false;
}
return true;
}
private boolean internetOffer() {
return getSingleOption() == 1;
}
private boolean phoneOffer() {
return getSingleOption() == 0;
}
private int getSingleOption() {
int i = -1;
for(int j; j =0; j++) {
if(options[j]) {
if ( i >= 0) {
return -1; //user has selected > 1 option
} else {
i = j;
}
}
}
return i;
}
}
I'm guessing the boolean[] won't be popular, but I think having such a structure gives you an easy way to determine how many options the user has flagged as true, which from your question seems like something you'd want to know.

Sorry not enought reputation to comment,
first of all you can use switches: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html, it is much easier for codes like this one, and maybe for organization you can use another function that is launched from this one (Just remember to put that strings univeral)

Related

Is it possible to insert a new condition to a while loop mid-loop in java?

I have a while(notFound) loop. This loop first checks stuff that specified in option "a" however some point in the loop that option becomes "b" and in that case I need to somewhat update the loop to:
while((notFound) &&(trCollator.compare(data.getSurname("a"),current.data().getSurname("a")) == 0))
So are there any syntax that cheks the second part only if the option equals "b"?
You can do something like:
while((notFound)){
if(option.equals("a") || (option.equals("b") &&(trCollator.compare(data.getSurname("a"),current.data().getSurname("a")) == 0))){
//execute code...
}
}
You can toogle an additional condition using a boolean implication:
»*If ... then ...*« which is the same as !... || ....
String option = "a";
while (notFound && (!"b".equals(option) ||
trCollator.compare(data.getSurname("a"), current.data().getSurname("a")) == 0))
{
// do stuff
}
Just use a flag that says whether you need to check it or not.
boolean flag = false;
while (notFound &&
(!flag || trCollator.compare(data.getSurname("a"),current.data().getSurname("a") == 0)) {
...
if (somethingOrOther)
flag = true;
...
}
Initially, flag is false, so !flag is true, so the second clause of the logical-or operator is skipped. Later, !flag is false, so the second clause is evaluated.
// create an interface
public interface myInterface {
public boolean compareTo();
}
// create two classes
public class aa implements myInterface {
#Override
public boolean compareTo() {
// TODO Auto-generated method stub
return true;
}
}
public class bb implements myInterface {
String surname;
String currentSurname;
bb(String surname, String currSurname) {
this.surname = surname;
this.currentSurname = currSurname;
}
#Override
public boolean compareTo() {
// TODO Auto-generated method stub
return this.surname.equals(this.currentSurname);
}
}
// in while loop when you hit some condition change the value of mObj like below
Scanner n = new Scanner(System.in);
int tmp =n.nextInt();
myInterface mObj = new aa();
while(tmp > 0 && mObj.compareTo()) {
// some condition
if(tmp == 5) {
mObj = new bb("kumar", "sharma");
}
tmp--;
System.out.println(tmp);
}
System.out.println("Done");

Performs a search by multiple values - using an arrayList object

I have a problem with java that I have been trying to solve for several hours and can not.
I have an object of Ad, I use this object with arrayList.
I want to select the object of Ad - which are inside the arrayList - I want to select the object according to its attributes, I do this in the function:
I get attributes that an ad object has - I want to filter the Ad by attributes.
public class filterAds {
public class Ad {
String domain;
String role;
String area;
public Ad(String domain, String role, String area) {
this.area = area;
this.domain = domain;
this.role = role;
}
}
List<Ad> adList = new ArrayList<Ad>();
public String[] getAds(String role, String domain, String area) {
boolean filter = true;
if(role != null)
{
//use it in search
}
if(area != null)
{
//use it in search
}
if(domain != null)
{
//use it in search
}
List<String> adIDsList = new ArrayList<String>();
for (int i = 0; i < adList.size(); i++) {
if (filter /* && also other conditions*/) {
adIDsList.add(adList.get(i).id);
}
}
String[] adIDs = new String[adIDsList.size()];;
adIDs = adIDsList.toArray(adIDs);
return adIDs;
}
}
I think the problem is not big, just need to fix the if conditions - but I have not been able to for hours.
quite standard way to organize such search is:
List<String> adIDsList = new ArrayList<String>();
for (int i = 0; i < adList.size(); i++) {
Ad ad = adList.get(i);
if (
(role == null || role.equals(ad.role)) &&
(area == null || area.equals(ad.area)) &&
(domain == null || domain.equals(ad.domain))
) {
adIDsList.add(ad.id);
}
}
so, we handle null and non-null in the same condition
You can use stream api to filter data from list.
Below is the rough code that may give you idea how you can do.
List<String> adIDsList = adList.stream()
.filter(ad -> role.equals(ad.role))
.filter(ad -> domain.equals(ad.domain))
.filter(ad -> area.equals(ad.area))
.map(Ad::id)
.collect(Collectors.toList());

Are there anyway to reduced complexiy of following java code?

I need to reduced following java method complexity according to sonar acceptable level. now ,it given this like sonar issue.
need some expert help to do this.
public List<X> Y(final DateTime treatmentDiscontinueTime,
final List< P> validPrescribedPrescriptions)
{
final List<x> doseWrapperList = new ArrayList<>();
final int noOfPrescriptions = validPrescribedPrescriptions.size();
for (int prescriptionIndex = 0; prescriptionIndex < noOfPrescriptions; prescriptionIndex++)
{
final BasePrescribedPrescription basePrescribedPrescription = validPrescribedPrescriptions.get(prescriptionIndex);
final String firstDoseText = basePrescribedPrescription.getFirstText();
final String secondDoseText = basePrescribedPrescription.getSecondText();
final boolean accordingToSchedule = A.ACCORDING.equals(firstDoseText);
final boolean specificPrescription = A.SP.equals(firstDoseText);
final boolean specificVbTypePrescription = A.SPVB.equals(firstDoseText);
List<D> doseDetails = new ArrayList<>(basePrescribedPrescription.getDoseDetails());
final DateTime changedDosageEndDate =
getChangedDoseEndDate(basePrescribedPrescription.getActualTerminateDate(), treatmentDiscontinueTime);
final int noOfDosages = doseDetails.size();
for (int doseIndex = 0; doseIndex < noOfDosages; doseIndex++)
{
final D doseDetail = doseDetails.get(doseIndex);
if ((doseDetail.getStart().getStartDate() != null) && (changedDosageEndDate != null) &&
doseDetail.getStart().getStartDate().isAfter(changedDosageEndDate))
{
continue;
}
String previewDoseText;
if (accordingToSchedule)
{
previewDoseText = X
}
else if (specificPrescription)
{
previewDoseText = Y;
}
else if (specificVbTypePrescription)
{
previewDoseText = Z;
}
else if (noOfDosages == 2)
{
previewDoseText = ((doseIndex == 0) ? secondDoseText : firstDoseText);
}
else
{
previewDoseText = firstDoseText;
}
final boolean isUnplanned =isuNplaned()
if (!isUnplanned)
{
doseStart = getStartDate();
doseEnd = getEndDate();
}
doseWrapperList.add(new DoseInfoLiteDTOWrapper(previewDoseText, doseStart, doseEnd, doseDetail));
}
}
return doseWrapperList;
}
i need some expert help to resoled this sonar issue. I thing different way to extract code fragment , breakdown this method to little parts.but still couldn't find some proper way to do it.
It's not difficult to clear, i think:
Use simple For loop
Create more small methods to do small(clear) things for For loop
Block if/esle: use simple statement
Hint: Study TDD to write clean code as possible

If-else with two possible ways

Could you please help me find a solution for my code? I'm making a new Android app in which I need to make some calculations and the scenario is the following:
There are four fields to be calculated. Two EditText (number decimal) field are obligatory and the other two are optional, BUT, if the optional fields are filled, then it needs to be in the calculation, otherwise only the obligatory fields will be used.
Right now I'm totally OK with calculating the obligatory fields but when I try some if-else clause to include the optional fields in the calculation, the app goes bananas.
I'm not sure where I should make this two-step option, if I should use boolean to check the option field condition, if I just keep using if-else...
The problem is not the calculatin itself, but having two ways for the code to follow: One using only the obligatory fields if nothing else is inserted and the other one using all four fields.
Thanks everyone!
Code below is only using the two obligatory fields.
public void calcularResultado(View view) {
//check for blank values in obligatory fields
if (editGasolina.length() == 0) {
editGasolina.setError("Insira o valor");
}
if (editEtanol.length() == 0) {
editEtanol.setError("Insira o valor");
//runs the code
} else {
double valorGasolina = Double.parseDouble(editGasolina.getText().toString());
double valorEtanol = Double.parseDouble(editEtanol.getText().toString());
double valorResultado = valorEtanol / valorGasolina;
double porcentagem = (valorResultado) * 100;
String valorResultadoTexto = Double.toString(porcentagem);
valorResultadoTexto = String.format("%.2f", porcentagem);
if (valorResultado >= 0.7) {
textResultado.setText("GASOLINA");
textRendimento.setText(valorResultadoTexto + "%");
} else {
textResultado.setText("ETANOL");
textRendimento.setText(valorResultadoTexto + "%");
}
You almost got it. What happens now, since you have an if-if-elseconstruction, it considers the first if statement to be seperate from the if-else block below. That is to say, if editEtanol.length() == 0 evaluates to false, it will execute the else block below, even if editGasolina.length() == 0 evaluates to true.
Changing the line if (editEtanol.length() == 0) { to else if (editEtanol.length() == 0) { should already help alot. Hope that helps!
public void calcularResultado(View view) {
//check for blank values in obligatory fields
if (editGasolina.length() == 0) {
editGasolina.setError("Insira o valor");
}
if (editEtanol.length() == 0) {
editEtanol.setError("Insira o valor");
//runs the code
} else {
double valorGasolina = Double.parseDouble(editGasolina.getText().toString());
double valorEtanol = Double.parseDouble(editEtanol.getText().toString());
boolean optionalField1Used = optionalEditText1.length() != 0;
boolean optionalField2Used = optionalEditText2.length() != 0;
double valorResultado = 0;
if (!optionalField1Used && !optionalField2Used) {
valorResultado = valorEtanol / valorGasolina;
} else if (optionalField1Used && !optionalField2Used) {
valorResultado = //some other calculation
} else if (!optionalField1Used && optionalField2Used) {
valorResultado = //yet another calculation
} else {
valorResultado = //calculation if both optional fields used
}
double porcentagem = (valorResultado) * 100;
String valorResultadoTexto = Double.toString(porcentagem);
valorResultadoTexto = String.format("%.2f", porcentagem);
if (valorResultado >= 0.7) {
textResultado.setText("GASOLINA");
textRendimento.setText(valorResultadoTexto + "%");
} else {
textResultado.setText("ETANOL");
textRendimento.setText(valorResultadoTexto + "%");
}
Let us assume that the optional fields are called edit1 and edit2. I also assume that in order to use the alternative computation, both optional values must be present.
To enhance code clarity, I would define two Boolean variables to explicitly indicate whether the mandatory and optional fields have values. Something like the following.
public void calcularResultado(View view) {
var mandatoryValues = true;
var optionalValues = false;
if (editGasolina.length() == 0 {
editGasolina.setError("Insira o valor");
mandatoryValues = false;
}
if (editEtanol.length() == 0 {
editEtanol.setError("Insira o valor");
mandatoryValues = false;
}
if (edit1.length() > 0 && edit2.length() > 0) {
optionalValues = true;
}
if (mandatoryValues) {
if (optionalValues) {
// do alternative computation
} else {
// do computation for mandatory values only
}
}
}
Note that if either mandatory value is absent, no computation is performed.
Hope it helps - Carlos

Copy constructor help, trying to copy a boolean array. Java

I have looked through as many previous questions as possible but never saw a question that had a boolean array as a variable.
Here is my class:
public class Register {
private boolean[] register;
private int length;
//Normal constructor
public Register(int n) {
if (n == 8 || n == 16 || n == 32 || n == 64) {
length = n;
register = new boolean[length];
for (int i = 0; i < length; i++) {
register[i] = false;
}
} else {
throw new RegisterException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
// Creates a copy of reg (an existing Register)
public Register(Register reg) {
length = reg.length;
register = new boolean[reg.register.length];
System.arraycopy(reg.register, 0, this.register, 0, reg.register.length);
}
In my driver program i am loading "1101101" into register1, but when i do:
Register register2 = new Register(register1);
and print out both results i get:
0000000001101101
0000000000010110
Not really sure what is going on O.o any help would be appreciated, thanks!
This is my load method. i held off on putting it in here because it might be hard to read:
public void load(String binaryRep) {
String allTheBits = binaryRep;
int charPosition = 0;
int loadLength;
int binaryNum = 0;
String index = "";
String trimmedIndex = "";
if (allTheBits.length() > 0 && allTheBits.length() <= length) {
loadLength = allTheBits.length();
for (int i = length - (loadLength); i < length; i++) {
index = allTheBits.charAt(charPosition) + "";
trimmedIndex = index.trim();
binaryNum = Integer.parseInt(trimmedIndex);
if (binaryNum == 1) {
register[i] = true;
} else if (binaryNum == 0) {
register[i] = false;
}
charPosition++;
}
} else {
throw new RegisterException("You can only load 0 - " + length
+ "bits.");
}
}
Here's a more idiomatic way of doing it (using the Cloneable interface):
public class Register implements Cloneable {
private boolean[] register;
public Register(boolean[] register) {
int n = register.length;
if (n == 8 || n == 16 || n == 32 || n == 64) {
this.register = register;
} else {
throw new IllegalArgumentException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for ( boolean b : this.register ) {
builder.append( b ? "1" : "0" );
}
return builder.toString();
}
public Register( int n ) {
this( new boolean[n] );
}
public int getLength() {
return this.register.length;
}
#Override
public Register clone() {
boolean[] clonedRegister = new boolean[this.register.length];
System.arraycopy(this.register, 0, clonedRegister,0, this.register.length);
return new Register( clonedRegister );
}
}
And a JUnit test showing it in action:
import org.junit.Assert;
import org.junit.Test;
public class RegisterTest {
#Test
public void testRegisterToString() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, true, false } );
String result = "11001010";
Assert.assertEquals( result, source.toString() );
}
#Test
public void testRegisterCloning() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, false, false } );
Register clone = source.clone();
Assert.assertEquals( source.toString(), clone.toString() );
}
}
A couple of remarks so that you learn some basic things.
As #Ted said, no need to keep the length field as register. length will give you the same
Local variables are not initialized with default values, but arrays are, as they are stored in the heap. So there's no need to iterate over the "register" array to set all of its positions to false
Using an array of booleans to do this may have felt easy but its extremely inefficient memory wise, as each boolean takes at least 32 bits in the heap. Therefore, to represent a 64 bit register you are using at least 32*64+32=2080 bits... using a byte array and bitwise logic will be a bit harder but hey, it's a small challenge :)
Anyway, your code looks fine (BTW, use Arrays.copyOf method as it's more readable), so the error should be coming from another side.
I just verified your load method with the following:
public static void main(String [] args)
{
Register r1 = new Register(8);
r1.load("1101101");
Register r2 = new Register(r1);
for (int i=0; i<8; i++) System.out.println(r2.register[i]);
}
Output:
> run Register
false
true
true
false
true
true
false
true
>
It looks right to me as far as the contents of the Register objects are concerned, so the problem probably is with the access.

Categories