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've got a situation in which I need to check multiple conditions, where every combination has a different outcome. In my specific condition, I've got 2 variables, which are enum types, that can each be 2 different values.
enum Enum1
{
COND_1,
COND_2
}
enum EnumA
{
COND_A,
COND_B
}
Enum1 var1;
EnumA varA;
This gives me 4 possible conditions, which requires 4 different outcomes. I've come up with a few different ways of doing this, either using if statements or switch statements:
if(var1 == Enum1.COND_1 && varA == EnumA.COND_A)
{
// Code
}
else if(var1 == Enum1.COND_1 && varA == EnumA.COND_B)
{
// Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_A)
{
// Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_B)
{
// Code
}
Or:
switch(var1)
{
case COND_1:
switch(varA)
{
case COND_A:
// Code
break;
case COND_B:
// Code
break;
}
break;
case COND_2:
switch(varA)
{
case COND_A:
// Code
break;
case COND_B:
// Code
break;
}
break;
}
I've thought of others, but don't want to fill this up with code :P I'd like to know what the best way to do this is. I think the switch is a bit easier to read, but the ifs are shorter. I think it'd be really cool if switches could have multiple conditions, but I haven't heard of it. This also begs the question: what's the best way to do this with an arbitrary number of variables and possible values?
For your small use case I would probably go for nested if statements. But if you have plenty of enum constants, perhaps a pattern using streams could make your code easier to read and maintain (for a small performance penalty). You could solve it using a stream like this:
Stream.of(new Conditional(COND_1, COND_A, () -> {/* do something */}),
new Conditional(COND_1, COND_B, () -> {/* do something */}),
new Conditional(COND_2, COND_A, () -> {/* do something */}),
new Conditional(COND_2, COND_B, () -> {/* do something */}))
.filter(x -> x.test(var1, varA))
.findAny()
.ifPresent(Conditional::run);
That would require a supporting class:
class Conditional implements BiPredicate<Enum1, EnumA>, Runnable
{
private final Enum1 var1;
private final EnumA varA;
private final Runnable runnable;
public Conditional(Enum1 var1, EnumA varA, Runnable runnable) {
this.var1 = var1;
this.varA = varA;
this.runnable = runnable;
}
#Override
public boolean test(Enum1 enum1, EnumA enumA) {
return var1 == enum1 && varA == enumA;
}
#Override
public void run() {
runnable.run();
}
}
Performance differences are probably negligible here, so I would focus on shortness and readability. So I would just simplify the if's a bit by using temporary variables:
boolean is_1 = (var1 == Enum1.COND_1);
boolean is_A = (varA == EnumA.COND_A);
if(is_1 && is_A)
{
// Code
}
else if(is_1 && !is_A)
{
// Code
}
else if(!is_1 && is_A)
{
// Code
}
else if(!is_1 && !is_A)
{
// Code
}
I prefer the if variant without nesting, since it is short and you have all the conditions in one line.
When stopping through the code during debugging, it can get tedious though, since you have to step over all preceding conditions, which is O(n). When executing the code, this shouldn't matter since the compiler will probably optimize the code.
There is no obvious best way, so you will have to experiment a bit.
I definitely prefer the flat version, it could just use a little less duplication:
// If you can't make the variables final, make some final copies
final Enum1 var1 = Enum1.COND_2;
final EnumA varA = EnumA.COND_B;
class Tester { // You could also make an anonymous BiPredicate<Enum1, EnumA>
boolean t(Enum1 v1, EnumA vA) {
return var1 == v1 && varA == vA;
}
};
Tester tes = new Tester();
if (tes.t(Enum1.COND_1, EnumA.COND_A)) {
// code
} else if (tes.t(Enum1.COND_1, EnumA.COND_B)) {
// code
} else if (tes.t(Enum1.COND_2, EnumA.COND_A)) {
// code
} else if (tes.t(Enum1.COND_2, EnumA.COND_B)) {
// code
}
Run it here. You could maybe make it even shorter and less redundant by doing a static import of the enums to avoid mentioning the enum names, e.g. tes.t(COND_1, COND_B). Or if you're willing to give up some compile time safety you can pass a string which gets converted to the two enum values, e.g. tes.t("COND_1 COND_A") (the implementation is left to the reader).
Maybe crazy idea but you could construct an int or a byte using the flags and use it in a single switch.
private int getIntegerStateForConditions(boolean... conditions ){
int state = 0;
int position = 0;
for(boolean condition: conditions){
if(condition){
state = state || (1 << position++);
}
}
return state;
}
...
switch(getIntegerStateForCondition((var1 == Enum1.COND_1), (var2 == EnumA.COND_A)){
case 0: ... //both condition false
case 1: ... //first condition true second false
case 2: ... //first false, second true ...
}
...
I think this is very far from being clean code but it looks better.
If I were you I would rely on bit flags in order to have only one byte (as you have only 4 use cases) to deal with and use a switch statement on this byte to manage all your use cases.
Something like this:
private static final int COND_2 = 1;
private static final int COND_B = 2;
private byte value;
public void setValue(Enum1 enum1) {
if (enum1 == Enum1.COND_1) {
this.value &= ~COND_2;
} else {
this.value |= COND_2;
}
}
public void setValue(EnumA enumA) {
if (enumA == EnumA.COND_A) {
this.value &= ~COND_B;
} else {
this.value |= COND_B;
}
}
public Enum1 getEnum1() {
return (this.value & COND_2) == COND_2 ? Enum1.COND_2 : Enum1.COND_1;
}
public EnumA getEnumA() {
return (this.value & COND_B) == COND_B ? EnumA.COND_B : EnumA.COND_A;
}
Then your tests would be:
switch (value) {
case 0 :
// 1-A;
break;
case 1 :
// 2-A;
break;
case 2 :
// 1-B;
break;
case 3 :
// 2-B;
break;
}
I would personally prefer this:
if(understandableNameInContextName1(var1, varA))
{
// Code
}
else if(understandableNameInContextName2(var1, varA))
{
// Code
}
else if(understandableNameInContextName3(var1, varA))
{
// Code
}
else if(understandableNameInContextName4(var1, varA))
{
// Code
}
private boolean understandableNameInContextName1(Object var1, Object varA){
return (var1 == Enum1.COND_1 && varA == EnumA.COND_A);
}
private boolean understandableNameInContextName2(Object var1, Object varA){
return (var1 == Enum1.COND_1 && varA == EnumA.COND_B);
}
private boolean understandableNameInContextName3(Object var1, Object varA){
return (var1 == Enum1.COND_2 && varA == EnumA.COND_A);
}
private boolean understandableNameInContextName4(Object var1, Object varA){
return (var1 == Enum1.COND_2 && varA == EnumA.COND_B);
}
And the names of the methods could be like, isOrderShippedAndDelivered(), isRequestSendAndAckRecieved().
The reason is that this is going to make the code a lot more readable.
Unless you have data that leads you back to these if statement there is not going to be much gain optimizing these.
See:
https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil
Kind of depends on the complexity of the code and number of combinations but another option is a dictionary with the key comprising a Tuple of your enumerations and a value of a delegate to the code.
Related
I have 6 values in the enum and using 6 if-else is really a bad practice.
Can we implement this in any better way? Below is my scenario :
ExampleEnum value = getEnumValue();
if(ExampleEnum.A == value){
doA();
}else if(ExampleEnum.B == value){
doB();
}else if(ExampleEnum.C == value){
doC();
}else if(ExampleEnum.D == value){
doD();
}else if(ExampleEnum.E == value){
doE();
}else if(ExampleEnum.F == value){
doF();
}
I was thinking of switch, but is is not making much difference also i need to return a boolean value inside doA() depending on certain parameters.
Thanks in advance.
You have a few options:
A chain of else-ifs
Leave your code as-is. Hard to read and write.
Switch
switch (value) {
case A:
doA();
break;
case B:
doB();
break;
case C:
doC();
break;
case D:
doD();
break;
case E:
doE();
break;
case F:
doF();
break;
}
Note that this is the classic switch. If you have access to newer Java versions, it is probably possible to get rid of the breaks.
EnumMap
You can also create an EnumMap:
EnumMap<ExampleEnum, Runnable> enumMap = new EnumMap<>(Map.<ExampleEnum, Runnable>of(
ExampleEnum.A, Main::doA, // 'Main', or wherever your do* methods are.
ExampleEnum.B, Main::doB,
ExampleEnum.C, Main::doC, // I'm using method references. But you could
ExampleEnum.D, Main::doD, // also use lambda expressions: '() -> doD()'.
ExampleEnum.E, Main::doE,
ExampleEnum.F, Main::doF
));
ExampleEnum value = getEnumValue();
enumMap.get(value).run();
If you want to use a switch statement and you're on Java 12 or newer, consider using extended switch expressions that avoid the pitfalls of break statements:
switch (value) {
case A -> doA();
case B -> doB();
case C -> doC();
case D -> doD();
case E -> doE();
case F -> doF();
}
You can add the do method inside the enum.
public enum ExampleEnum {
A {
public void doIt() { ... }
},
B {
public void doIt() { ... }
},
...
abstract public void doIt();
}
ExampleEnum value = getEnumValue();
if (value != null) {
value.doIt();
}
I am writing some java code to check multiple conditions by if-else. The code is working properly but it is hard to do unit test.
reads lines that contains keyword conditionOne, conditionTwo or other keywords. hasConditionOneEnabled and hasConditionTwoEnabled are boolean values.
My real code has more else if statements than the provide example.
Can anyone help? Or give me some hint how to make the code shorter then I can write unit test easier? Thanks
boolean a = false;
boolean b = false;
if(line.contains("conditionOne")){
if(hasConditionOneEnabled){
a = true;
}else{
b = true;
}
}else if (line.contains("conditionTwo")){
if(hasConditionTwoEnabled){
a = true;
}else{
b = true;
}
}else{
a = true;
b = true;
}
if(a && b){
// do something 1
}else if(!a && b){
// do something 2
}else if(a && !b){
// do something 3
}else{
//both false, do nothing
}
a and b cannot be both false after the set of if-else statements.
In the first two if's variable a will have the same value than the corresponding hasConditionXXEnabled and b will be set as the opposite. The default else will set both to true.
Consider the following code:
a = true;
b = true;
if(line.contains("conditionOne")){
a = hasConditionOneEnabled;
b = !a;
}
else if(line.contains("conditionTwo")){
a = hasConditionTwoEnabled;
b = !a;
}
if(a && b){
// do something 1
}
else if(b){
// do something 2
}
else{
// do something 3
}
// test it on different line String input and different int value returned...
int xxx(String line) {
if(line.contains("conditionOne")){
status = hasConditionOneEnabled?0:1;
} else if (line.contains("conditionTwo")){
status = hasConditionTwoEnabled?0:1;
} else{
status = -1;
}
return status;
}
// test it base on different status value..
switch (status) {
case 0: ...;
case 1: ...;
default: ...;
}
However, if your if-else pattern can be continuously repeat after some modification, you may just create different boolean funciton for it.
First of all both a and b can never be false, so your last else statement is redundant.
Your entire set of conditional statements can be reduced to an if - else if - else block. You don't need variables a and b since you are using them to do something else anyway. Besides vague variables names like a and b hinder readability.
Let me first show you the code and I'll walk you through it subsequently.
boolean lineContainsCond1 = line.contains("conditionOne");
boolean lineContainsCond2 = line.contains("conditionTwo");
boolean lineContainsNeitherCondition = !lineContainsCond1 && !lineContainsCond2;
boolean conditionsForSomething3 = (lineContainsCond1 && conditionOneEnabled) || (lineContainsCond2 && conditionTwoEnabled);
if(lineContainsNeitherCondition)
//do something 1 (Note: this is the same something 1 from your code)
else if(conditionsForSomething3)
//do something 3
else
//do something 2
lineContainsNeitherCondition is essentially both a and b being true in your code.
conditionsForSomething3 tantamounts to a!b.
If both lineContainsNeitherCondition and conditionsForSomething3 are false, we can derive the following conclusions:
Given lineContainsNeitherCondition is false, either lineContainsCond1 is true or lineContainsCond2 is true
Case 1 : lineContainsCond1 is true:
In this case, either conditionOneIsEnabled is true or conditionOneEnabled is false. If it were true, then conditionFOrSomething3 cannot be false, if it's false, then that leads to lineContainsCond && !conditionOneEnabled to be true which leads to b!a in the original code and thereby executes //something 2.
A similar argument can be made for Case 2 : lineContainsCond2 is true.
Why don't reduce the amount of if else statements in your code.
Try replacing the if else statements with private methods that return a boolean. Try to in cooperate the below methods or similar methods into your above code.
Having a look at mookito great for mocking and stubbing. If you have a big project with lots of Objects will save you hours maybe days.
private boolean doesLineContainCondition(String line, String searchPhrase) {
if(line.contains(searchPhrase) {
return true;
} else {
return false;
}
}
private boolean hasConditionBeenEnabled(boolean condition) {
if(condition) {
a = true;
}
else {
b= true;
}
}
I learned about terminary expression, but what I want is a little different.
I have the following:
int MODE = getMyIntValue();
I do comparison as the following:
if(MODE == 1 || MODE == 2 || MODE == 3) //do something
I would like to know if there is a short way of doing this, I tried something like this but it didn't work:
if(MODE == 1 || 2 || 3) //do something
There is a short|quick way of doing it? I like quick "ifs" because it makes the code more clear, for example, it is more clear this:
System.out.println(MODE == 1 ? text1 : text2):
Than this:
if(MODE == 1) System.out.println(text1):
else System.out.println(text1):
Thanks in advance!
May be you can do something like this
System.out.println(Mode == 1 ? "1" : Mode == 2 ? "2" : "3");
switch-case also makes code more readable than multiple if-else
Well, if you don't mind the boxing hit, you could use a set which you prepared earlier:
// Use a more appropriate name if necessary
private static final Set<Integer> VALID_MODES
= new HashSet<>(Arrays.asList(1, 2, 3));
...
if (VALID_MODES.contains(mode)) {
}
You could use an int[] and a custom "does this array contain this value" method if you wanted... it would be O(N) or O(log N) for a binary search, but I suspect we're talking about small sets anyway.
I strongly recommend to use a more typed approach:
public class QuickIntSample {
enum Modes {
ONE(1),TWO(2),THREE(3); // you may choose more useful and readable names
int code;
private Modes(int code) {
this.code = code;
}
public static Modes fromCode(final int intCode) {
for (final Modes mode : values()) {
if (mode.code == intCode) {
return mode;
}
}
return null;
}
} // -- END of enum
public static void main(String[] args) {
int mode = 2;
if( Modes.fromCode(mode) == Modes.TWO ) {
System.out.println("got code 2");
}
}
}
I'm writing a method along these lines:
if (hasFoo()) {
return calculateFoo();
} else if (hasBar()) {
return calculateBar();
} else {
return calculateBaz();
}
The getters are rather expensive and the has...() checks would either duplicate a lot of the logic or just have to reuse the getters. I could have the has...() methods store the result of the get...() in a field and make the getter lazy, but it would be nice for has...() not to have any side effects. I could write this with nested try{} catch{} blocks, but that doesn't look elegant. seems like there should be a better solution to this...
EDIT: changed get...() to calculate...() to make it clear that they're expensive.
int result = 0;
if (hasFoo()) {
result = getFoo();
} else if (hasBar()) {
result = getBar();
} else {
result = getBaz();
}
return result;
is the idiom I prefer to use - makes it far easier to inspect variable values while debugging.
I see nothing wrong in doing
Object fooBarBaz = null;
if (hasFoo()) {
foo = getFoo();
} else if (hasBar()) {
fooBarBaz = getBar();
} else {
fooBarBaz = getBaz();
}
return fooBarBaz;
I prefer it this way:
if (hasFoo()) {
return calculateFoo();
}
if (hasBar()) {
return calculateBar();
}
return calculateBaz();
All a matter of taste and convention.
I am not sure if this is your case, but I would try to fully refactor the code. Currently, as far as I understand, your code looks something like this (example):
boolean hasFoo() {
DataObject do = getSomeDataSource().getSomeDataObject();
if (do.getF() != null && do.getO() != null) {
return true;
} else {
return false;
}
}
Foo getFoo() {
DataObject do = getSomeDataSource().getSomeDataObject();
Foo result = new Foo(do.getF(), do.getO());
return result;
}
Basically what happens here is that the same code is used to check if Foo can be returned and to construct the Foo itself too. And I would refactor it to this:
/**
* #returns instance of Foo or null if Foo is not found
*/
Foo getFoo() {
DataObject do = getSomeDataSource().getSomeDataObject();
F f = do.getF();
if (f == null) {
return null; //Foo can not be created
}
O o = do.getO();
if (o == null) {
return null; //Foo can not be created
}
return new Foo(f,o);
}
Now your original code would become similar to this:
Result r;
r = getFoo();
if (r == null) {
r = getBoo();
}
if (r == null) {
r = getDoo();
}
return r;
This is not an "is it OK to do multiple returns" problem - your multiple returns are fine.
This is a refactoring and/or state storage problem.
If you have:
bool hasXXX() {
// do lots of stuff
...
return has_xxx;
}
and
double calculateXXX() {
// do the same lots of stuff
...
// do some more stuff
...
return xxx;
}
then the complexity of the problem depends on whether the hasXXX() calculation produces lots of intermediate values that are necessary for calculateXXX().
You likely need something like:
bool checked_xxx = false;
double xxx_state;
bool hasXXX() {
// do expensive stuff
...
// save temporary state variables
xxx_state = ...
// remember that we've been here
checked_xxx = true;
// send back the required value
return has_xxx;
}
double calculateXXX() {
// make sure that hasXXX was called, and is valid
if (!checked_xxx && !hasXXX()) {
// should never happen - you called calculateXXX when hasXXX() == false
throw new RuntimeException("precondition failed");
}
// use the previously calculated temporary state variables
...
// send back the final result
return xxx;
}
EDIT: If I'm interpreting your comments correctly, it sounds like you actually want something like:
Result result = calculateFoo();
if (result != null) {
return result;
}
result = calculateBar();
if (result != null) {
return result;
}
return calculateBaz();
... where each of the calculate methods returns null if the corresponding has method returns false. Now if null is a valid "real" return value, you could always wrap the result so that calculateFoo returns a value which can basically say, "Yes, I've got a valid value and it's X" or "no, I haven't got a valid value" (a "maybe" type).
Original answer
I would keep your code exactly as it is. I see no problems with having multiple return statements when that's the clearest approach - and in this case I believe it is.
You're making it clear that once you've reached each of the "leaf" parts, you know exactly what the return value is, and the only other code which should be executed before leaving the method is any clean-up code in finally blocks.
Having a single exit point makes sense in languages which don't have try/finally or GC (where you really want to make sure you do all the cleanup in a single place) but in Java, I think returning when you know the result states your intention more clearly than using a separate local variable.
Having said that, another alternative to consider is using the conditional operator, laying out your code so it's obviously going through a series of tests and returning as soon as it finds the first "match":
return hasFoo() ? getFoo()
: hasBar() ? getBar()
: getBaz();
The disadvantage is that this pattern looks a little odd the first time you see it - but once you get used to it, I find it a really neat way of encoding this sort of logic.
Instead of doing hasXXX() and calculateXXX() you could factor those calculations out to separate objects eg
public interface CalculationModel {
Object calculate();
}
public class FooCalculationModel implements CalculationModel {
#Override
public Object calculate() {
// Perform Foo calculations
return result;
}
}
and your if-statement can then be replaced with:
return getCalculationModel().calculate();
You will need some way of deciding the CalculationModel of course, but this would then replace the hasFoo(), hasBar() etc methods.
you could do something like this :
Object bar;
if ((bar = getFoo()) != null) {
return bar;
} else if ((bar = getBoo()) != null) {
return bar;
} else {
return getBaz()
}
this way you only need to call the get methods, but not the has ones
EDIT
this is the same in a more readable format that also elminates the need to call the has methods
Object bar = getFoo()
if (bar == null) {
bar = getBoo()
}
if (bar == null) {
bar = getBaz()
}
return bar;
I have these long statements that I will refer to as x,y etc. here.
My conditional statements' structure goes like this:
if(x || y || z || q){
if(x)
do someth
else if (y)
do something
if(z)
do something
else if(q)
do something
}
else
do smthing
Is there a better, shorter way to write this thing? Thanks
I don't see a big problem with how you write it now.
I do recommend using curly braces even for single statement if-blocks. This will help you avoid mistakes in case you have to add more code lines later (and might forget to add the curly braces then). I find it more readable as well.
The code would look like this then:
if (x || y || z || q) {
if (x) {
do something
} else if (y) {
do something
}
if (z) {
do something
} else if (q) {
do something
}
} else {
do something
}
Another variant that avoids the multiple checks and the errorprone complex logical expressions might be:
boolean conditionhandled = false;
if (x) {
do something
conditionhandled = true;
} else if (y) {
do something
conditionhandled = true;
}
if (z) {
do something
conditionhandled = true;
} else if (q) {
do something
conditionhandled = true;
}
if (!conditionhandled) {
do something
}
This seems pretty clear to me (and clear is good).
What you can do is first evaluate x,y,z and q and store those as variables so you don't have to do that twice.
Maybe this is a little easier to read. But now you will perform one extra check. If it is not mission critical code then maybe you can use the following:
if (x)
do something;
else if (y)
do something;
if (z)
do something;
else if(q)
do something;
if !(x || y || z || q)
do something completely different.
I'm not recommending the following, in fact, I think what you got is fine, but:
s = true;
if (x) {
do something;
s = false;
} else if (y) {
do something;
s = false;
}
if (z) {
do something;
s = false;
} else if (q) {
do something;
s = false;
}
if (s) {
so something;
}
Can you make some assumptions about x,y,z,q?
e.G. just one of them can be true. Than you could see it as a State
enum State {
X{
void doSomething(){
doItTheXWay();
}
},
Y{
void doSomething(){
doItTheYWay();
}
},
Z{
void doSomething(){
doItTheZWay();
}
},
Q{
void doSomething(){
doItTheQWay();
}
};
void doSomething(){
}
}
and in your code where you used the if statements
you could assign a state and just do the right thing
State state = getAState();
state.doSomething();
In case you don't like enums State could be an Interface and X to Q could be implementing classes.
The benefits in this case are in multiple usage of the same if else construct. Say some codelines later you would begin with
if(x)
do_the_next_thing_with_X();
...
or you could just extend your enum with another function and make one single call
state.doTheNextThing();