Converting Many 'If Else' Statements to a Cleaner Approach

Converting many 'if else' statements to a cleaner approach

You could have a Converter interface. Then you could create a class for each Mimetype like:

public interface Converter {

public void convertToMp3();
public void convertToOgg();

}

public class MpegConverter implements Converter {

public void convertToMp3() {
//Code here
}

public void convertToOgg() {
//Code here
}

}

You would need a class like this for each converter. Then you could set up a map like this:

Map<String, Converter> mimeTypeMap = new HashMap<String, Converter>();

mimeTypeMap.put("audio/mpeg", new MpegConverter());

Then your convertToMp3 method becomes like this:

Converter converter = mimeTypeMap.get(mimeType);
converter.convertToMp3();

Using this approach you could easily add different converters in the future.

All untested, probably doesn't compile, but you get the idea

Modifying duplicate if-else statements into a cleaner code

With java-8 you could make it quite fancy by introducing a new method which accepts the numbers and an DoubleBinaryOperator:

public void setAnswerText(double num1, double num2, DoubleBinaryOperator operator){
final double ans = operator.applyAsDouble(num1, num2);
final double doub = Math.round(ans * 100000.0) / 100000.0;
if(doub == (int) ans){
Answer.setText(Integer.toString((int) ans));
} else{
Answer.setText(Double.toString(doub));
}
}

and using a switch statement:

final double num1 = Double.parseDouble(FirstInput.getText());
final double num2 = Double.parseDouble(SecondInput.getText());
final String oper = (Operator.getText());
final DoubleBinaryOperator operator;
switch(oper){
case "+":
operator = (a, b) -> a+b;
break;
case "-":
operator = (a, b) -> a-b;
break;
case "/":
operator = (a, b) -> a/b;
break;
case "x":
operator = (a, b) -> a*b;
break;
default:
throw new UnsupportedOperationException();
}

setAnswerText(num1, num2, operator);

That way, you're finding the matching operator in the switch-statment and then executing the method with the numbers and the found operator

A cleaner if statement with multiple comparisons

Set<String> stuff = new HashSet<String>();
stuff.add("x");
stuff.add("y");
stuff.add("z");
if(stuff.contains(a)) {
//stuff
}

If this is a tight loop you can use a static Set.

static Set<String> stuff;
static {
stuff = new HashSet<String>();
stuff.add("x");
stuff.add("y");
stuff.add("z");
}

//Somewhere else in the cosmos

if(stuff.contains(a)) {
//stuff
}

And if you want to be extra sure nothing is getting modified while you're not looking.

Set<String> test = Collections.unmodifiableSet(new HashSet<String>() {
{
add("x");
add("y");
add("z");
}
});

If you just want to get some logic in there for a handful of hard coded conditions then one of the switch or if statement with newlines solutions might be better. But if you have a lot of conditions then it might be good to separate your configuration from logic.

Replacing if/else logic with state/strategy pattern

I think you should use GoF pattern Chain of responsibility. You should introduce two interfaces: 1) Condition where you will check proper condition, e.g. "If the zip file does not exist" and return boolean result - "true" if condition is satisfied, otherwise "else", 2) Execution strategy, that will run action assigned with condition, e.g. "download it from specified URL and then unzip it and read in file and move zip file to specified directory." So, 1st interface will be answer to the question "when", and 2nd - "then". "Condition" implementation and "execution strategy" implementation should be combined into "tuple" (or pair, entry, etc). This "tuple" should be moved to collection in order, that you've described. Then, when you need to handle zip file, you'll iterate over collection, invoking conditions and checking results, if result is "true" then invoking appropriate "execution strategy". Also, condition can be combined with execution strategy and moved into single interface/implementation with two methods. Context, that will describe current state of zip file can be passed between conditions/execution strategies.
Hope this helps.

Update.
Code example (in Java).

/**
* All implementations should check proper condition
*/
interface Condition {

/**
* Check if condition is satisfied
*
* @param pathToFile path to target file
* @return 'true' if condition is satisfied, otherwise 'false'
*/
boolean isSatisfied(String pathToFile); //i've made an assumption that you'll manipulate file path for checking file
}
...
/**
* Childs will wrap some portion of code (if you'll use language, that supports lambdas/functors, this interface/implementation can be replaced with lambda/functor)
*/
interface Action {

/**
* Execute some portion of code
*
* @param pathToFile path to target file
*/
void execute(String pathToFile);
}
...
class ZipFileExistsCondition implements Condition {

@Override
public boolean isSatisfied(String pathToFile) {
... //check if zip file exists
}
}
...
class ZipFileDoesNotExists implements Condition {
@Override
public boolean isSatisfied(String pathToFile) {
... //download zip file and move it to some temp directory
//if file downloaded ok, than return 'true' otherwise 'false'
}
}
...
class AlwaysSatisfiedCondition implements Condition {
@Override
public boolean isSatisfied(String pathToFile) {
... //always returns 'true', to run action assigned with this condition
}
}
...
Collection<Map.Entry<Condition, Action>> steps = Arrays.asList(
new AbstractMap.ImmutableEntry<Condition, Action>(new ZipFileExistsCondition(),
new Action() { /*move zip file to zip file directory and read in file*/ }),
new ZipFileDoesNotExists(), new Action() { /*download it from specified URL and then unzip it and read in file and move zip file to specified directory*/ },
new AlwaysSatisfiedCondition(), new Action() { /*create blank file and write it out to disk*/ }
);
...
String pathToFile = ...
...
for(Map.Entry<Condition, Action> step: steps) {
if(!step.getKey().isSatisfied(pathToFile))
continue;

step.getValue().execute(pathToFile);
}

Remarks:
1) you can implement 'Condition' as anonymous classes,
2) 'AlwaysSatisfiedCondition' can be singleton,
3) if you're using Java/Groovy/Scala, you can use Guava/Apache Commons 'Predicate' instead of 'Condition', 'Function' or 'Closure' instead of 'Action'.

If you need to exit after first 'satisfied' condition and appropriate action execution then just put 'break'/'return' after action execution.

How to refactor large if else block in java servlet front controller

Independently of the framework or technology, I've faced several times your case and what I always do is to use a command pattern:

https://en.wikipedia.org/wiki/Command_pattern

and in the command handler I use a Factory pattern that instantiates the appropriate class to handle the request with the information of the command object.

With this architecture you are veeery flexible and it is quite easy to implement :)

Replacing if else statement with pattern

This is a classic Replace Condition dispatcher with Command in the Refactoring to Patterns book.

Sample Image

Basically you make a Command object for each of the blocks of code in your old if/else group and then make a Map of those commands where the keys are your condition Strings

interface Handler{
void handle( myObject o);
}

Map<String, Handler> commandMap = new HashMap<>();
//feel free to factor these out to their own class or
//if using Java 8 use the new Lambda syntax
commandMap.put("conditionOne", new Handler(){
void handle(MyObject o){
//get desired parameters from MyObject and do stuff
}
});
...

Then instead of your if/else code it is instead:

 commandMap.get(someCondition).handle(this);

Now if you need to later add new commands, you just add to the hash.

If you want to handle a default case, you can use the Null Object pattern to handle the case where a condition isn't in the Map.

 Handler defaultHandler = ...

if(commandMap.containsKey(someCondition)){
commandMap.get(someCondition).handle(this);
}else{
defaultHandler.handle(this);
}

How to avoid many if-else statements

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


Related Topics



Leave a reply



Submit