Avoid simple if/else conditions [duplicate] - java

This question already has answers here:
How to avoid a lot of if else conditions
(8 answers)
Closed 4 years ago.
In my program, I need to check if a variable equals 1, 2 or 3 and depending on the result, perform a different method:
if (phase.equals("1")) {
PhaseOne.performPhase(inputParser.getSource(), inputParser.getTarget());
} else if (phase.equals("2")) {
PhaseTwo.performPhase(inputParser.getSource(), inputParser.getTarget());
} else {
PhaseThree.performPhase(inputParser.getSource(), inputParser.getTarget());
}
This code is so simple and basic but I really don't like it. Of course I could use switch conditions but it would, in my humble opinion, just display the same basic function in a different way.
My question is: is there a way to implement the function in an elegant and expandable way?
FYI, I already red this post but I did not find an answer which fits to my question.

In my opinion, the accepted answer on your linked question fits you very well. Store references to the functions in the map:
Map<String,BiConsumer<T,U>> map = new HashMap<>();
map.put("1",PhaseOne::performPhase);
map.put("2",PhaseTwo::performPhase);
map.put("3",PhaseThree::performPhase);
map.get(phase).accept(inputParser.getSource(), inputParser.getTarget());
Replace T and U by the types of inputParser.getSource() and inputParser.getTarget().
With this approach, the Phase… classes don't need a common superclass or interface.

If your PhaseOne/PhaseTwo/PhaseThree classes all implement the same interface (let's say Phase), and the method performPhase is defined on the interface you could do the following:
final Phase targetPhase;
switch(phase) {
case "1": targetPhase = myInstanceOfPhaseOne; break;
case "2": targetPhase = myInstanceOfPhaseTwo; break;
case "3": targetPhase = myInstanceOfPhaseThree; break;
default: throw new IllegalStateException("Unrecognised phase "+phase);
}
targetPhase.performPhase(inputParser.getSource(), inputParser.getTarget()));

Another option is to create a Class for each Phase and an IPhase Interface for them to implement.
Create a List<IPhase> with all your different Phase instances.
Run a loop and if the id matches, execute the overwritten method.
public interface IPhase {
public void performPhase();
public String getId();
}
for (IPhase phase : phasesList){
if (phase.equals(phase.getId())){
phase.performPhase();
// either break or continue the loop
}
}

Related

Java Error/Exception handling with returning value

So my friend and I are programming Blackjack in Java, and we wanted to test our input fields for the correct input(e.g only number input). So we sat at his PC and he wrote this solution:
public static boolean testeTextFieldInt(JTextField textField, int geld) {
if (!textField.getText().isEmpty()) {
try {
if(Integer.parseInt(textField.getText())>0 && Integer.parseInt(textField.getText())<geld ) {
return true;
}
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
now I disagree with this solution, because your code shouldn't depend on an error, or am I getting this wrong? so i sat down and wrote this:
public static boolean checkInput(JTextField textField, int spielerGeld, String eingabe) {
boolean matched = false;
switch (eingabe) {
case "num":
if (!textField.getText().isEmpty() && textField.getText().matches("^[0-9]*$")) {
int geldinput = Integer.parseInt(textField.getText());
if (geldinput > 0 && geldinput < spielerGeld) {
matched = true;
}
}
break;
case "string":
if (!textField.getText().isEmpty() && textField.getText().matches("^[a-zA-Z]*$")) {
matched = true;
}
break;
default:
break;
}
return matched;
}
Keep in mind, we yet dont have any textfields we have to check, but I just implemented it to get a grasp of how you could do multiple checks within one method.
So now my question is, what code is "better"? and what could we/I do better?
Thanks in advance!
EDIT1:
So as some already have mentioned, you say my method is not build up after the Single responsibility principle.
But if split up into 'checkInputIsnumber' and checkInputIsString' would the first solution(my friend), still be the "better" one?
EDIT2:
Better is defined as in, the method should be of low cyclomatic complexity, easy readability and be easy to maintain in the long run.
The first approach is much better than the second one.
Single responsibility: You should avoid creating methods that do more than one thing.
Open–closed principle: Your 'validation' is not extensible. Try creating a Validator interface and then an implementation per validation type.
Switch statements increase cyclomatic complexity and make testing harder.
Also, don't use textField.getText() everywhere, it's quite possible that it will change between calls. Assign it to a local variable or even better use a String as your argument and not JText. As Fildor pointed out you correctly avoid using exceptions for flow control and it is indeed better to have a single return point. Having said that, for simple cases when you just parse/check and return, it is acceptable.
You should put every check in a single function. After a while your "all in one function" will be unreadable an unmaintainable. Also it easier to change the checks if they are in single functions. Using try/catch for control flow is no good idea. It is expensive at runtime. It is not a good style and most developers won't expect control flow in a catch block.Excpetions are for exceptional situations.

Java: replace switch with lambdas. Worth it?

Using blocks of code with switch or if is a common thing when checking for events. It can be clean code when made simple, but still seems to have more lines than needed, and could be simplified using lambdas.
Block with if:
if(action == ACTION_1){
doAction1();
} else if(action == ACTION_2){
doAction2();
} else {
doDefaultAction();
}
Block with switch:
switch(action){
case ACTION_1:
doAction1();
break;
case ACTION_2:
doAction2();
break;
default:
doDefaultAction();
}
Block with lambdas using the utility class With below:
with(action)
.when(ACTION_1, this::doAction1)
.when(ACTION_2, this::doAction2)
.byDefault(this::doDefaultAction)
Using lambdas has less code, but the question is: is it easier to read than the others? Easier to maintain? Regarding performance lambdas is the worst, but for cases where performance is not important the lambdas version is shorter than the switch/if blocks.
So, how do you see it? Maybe there is a Kotlin way shorter than this, I try to focus on java only, I love Kotlin but the compilation is still too slow for my projects.
A similar utility class could be used when the block must return a specific value.
FYI, the class for the lambdas is here, I didn't check for errors, just made it quickly for this example:
public class With<T> {
private final T id;
private boolean actionFound;
private With(T id) {
this.id = id;
}
public static <T> With<T> with(T id) {
return new With<>(id);
}
public With<T> when(T expectedId, Action action) {
if (!actionFound && id == expectedId) {
actionFound = true;
action.execute();
}
return this;
}
public void byDefault(Action action) {
if (!actionFound) {
action.execute();
}
}
#FunctionalInterface
interface Action {
void execute();
}
}
As a couple has said, replacing switch with compounded methods is less efficient. Depending on your use-case, it might even be worth it to use your implementation.
Funnily enough, Oracle is actually planning to implement lambdas within switch statements, as seen in this recent JEP.
Example:
String formatted = switch (s) {
case null -> "(null)";
case "" -> "(empty)";
default -> s;
}
The switch is more flexible in that you can call functions with varying numbers of arguments, or call more than one function. You can also more easily denote when two cases lead to the same action. The fact that it's faster is just a bonus.
So in that sense I'm not sure what your With class is really adding.
However, switch has a limited number of types that it can work with. Perhaps your With class would prove to be more useful if you were to pass it predicates rather than performing simple reference equality, for example:
public With<T> when(Predicate<T> expected, Action action) {
if (!actionFound && expected.test(id)) {
actionFound = true;
action.execute();
}
return this;
}
Sample usage:
final String test = "test";
with(test)
.when(String::isEmpty, this::doAction1)
.when(s -> s.length() == 3, this::doAction2)
.byDefault(this::doDefaultAction);
replace switch with lambdas. Worth it?
No.
Because in an OO language the replacemenst for a switch or an if/else cascade is polymorphism, not "fluent API".
One option to do this is to declare static final Map<T, Action> EXPECTED_ID_TO_ACTION. Then you just can EXPECTED_ID_TO_ACTION.getOrDefault(actionId, DEFAULT_ACTION).execute(), turning ugly switch or multiple ifs into one-liner.

How to eliminate this particular SWITCH statement via Polymorphism

I have this switch statement which is executed after the user is shown a list of actions to take and clicks one of them. What we switch on is the action ID
switch (actionId) {
case 0:
openEditProductScreen();
break;
case 1:
startDeleteProductOperation();
break;
case 2:
//nothing
break;
case 3:
openAddProductScreen();
break;
}
I have read some articles on replacing switches with polymorphism but they relate to another type of problem - doing the same thing in different ways (the way you pay different types of employees), but what do I do when I want to trigger a completely different set of actions?
Thinking about it, do I really need to eliminate THIS particular kind of switch statement? I mean, it's readable and logical. What would the benefits be if I eliminated it somehow?
EDIT:
Is this what you meant?
private Map<Integer, ProductRelatedAction> productRelatedActions = new HashMap<Integer, ProductRelatedAction>();
private void mapActionsToIds() {
productRelatedActions.put(0, new EditProductAction());
productRelatedActions.put(1, new DeleteProductAction());
// remainder omitted
}
private abstract class ProductRelatedAction{
abstract void execute();
}
private class EditProductAction extends ProductRelatedAction{
#Override
void execute() {
openEditProductScreen();
}
}
private class DeleteProductAction extends ProductRelatedAction{
#Override
void execute() {
startDeleteProductOperation();
}
}
Add an abstract method execute() in the Action class, create 4 subclasses of Action, overriding execute(). Make the first one execute openEditProductScreen(), the second one execute startDeleteProductOperation(), etc.
Then create one instance of these 4 classes and make the user choose one of those 4 instances.
When the user has chosen the action, call selectedAction.execute().
Should you replace this kind of switch by polymorphism? In my opinion: yes. When you'll have to add another action, there is no way you'll be able to forget to implement the execute() method in the new subclass: your code won't compile without it. On the other hand, Forgetting to add a case in your switch statement is extremaly easy to do. And I'm not even mentioning the fall-through problem of switch statements.

function inside swith case - java programming

Im learning java and i have come across a function calc (have changed few objects and removed few lines), however i couldnt understand below code...
I understand this follows builder pattern
Questions:
i have never seen this before and due to poor search i havent got much help from googling... can we do return new Object and a function below that...
How this can be explained in simple terms
case 1 with no coding and default below that; does it mean 1 is most of the time default
Here is my code:
public calc(int value)
{
switch (value) {
case 0:
return new validator<objValidator>() {
#Override
public Boolean evaluate() {
//some business logic to return true/false
return true;
}
};
case 1:
default:
return new validator<objValidator>() {
#Override
public Boolean evaluate() {
//some business logic to return true/false
return true;
}
};
}
}
What you see here is called an anonymous inner class. Searching for that term should bring up some useful results.
Basically validator<objValidator> is an interface and you create an implementation of it in-place.
These are called as Anonymous Classes.
See this link.
Question 1: yes you can
think about it like defining a new Anonymous class inside it
for more info about Anonymous classes
please see this link
enter link description here
Question 2:
case 1: means no action done if the value is 1
case default: means if the value not = 1 then the function below will be executed
for more info about switch case statement
please point to this link

How to remove large if-else-if chain [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Long list of if statements in Java
I was tasked to work with some code, and there is a giant if-else-if chain (100+ else-ifs) that checks Strings.
What are some good techniques to update this code as to where the if-else-if chain can be shrunken down to something much more manageable.
The chain looks something like this:
if(name.equals("abc")){
do something
} else if(name.equals("xyz")){
do something different
} else if(name.equals("mno")){
do something different
} ......
.....
else{
error
}
You can extract the code in each branch to a separate method, then turn the methods into implementations of a common base interface (let's call it Handler). After that, you can fill a Map<String, Handler> and just look up and execute the right handler for given string.
Unfortunately the implementation of 100+ subclasses for the interface requires quite a lot of boilerplate code, but currently there is no simpler way in Java to achieve this. Implementing the cases as elements of an Enum may help somewhat - here is an example. The ideal solution would be using closures / lambdas, but alas we have to wait till Java 8 for that...
Some options / ideas:
Leave it as it is - it's not fundamentally broken, and is reasonably clear and simple to maintain
Use a switch statement (if you are using Java 7) - not sure if this gains you much though
Create a HashMap of String to FunctionObjects where the function objects implement the required behaviour as a method. Then your calling code is just: hashMap.get(name).doSomething();
Break it into a heirarchy of function calls by sub-grouping the strings. You could do this by taking each letter in turn, so one branch handles all the names starting with 'a' etc.
Refactor so that you don't pass the name as a String but instead pass a named object. Then you can just do namedObject.doSomething()
With Enums, you can have a method per instance.
public enum ActionEnum {
ABC {
#Override
void doSomething() {
System.out.println("Doing something for ABC");
}
},
XYZ {
#Override
void doSomething() {
System.out.println("Doing something for XYZ");
}
};
abstract void doSomething();
}
public class MyActionClass {
public void myMethod(String name) {
ActionEnum.valueOf("ABC").doSomething();
}
}
It is still kinda messy (big enum with 100+ entries, even it all it does is dispatching), but may avoid the HashMap initialization code (100+ puts is also messy in my opinion).
And yet another option (for documentation purposes) would be reflection:
public interface Action {
void doSomething();
}
public class ABCAction implements Action {
#Override
public void doSomething() {
System.out.println("Doing something for ABC");
}
}
public class MyActionClass {
void doSomethingWithReflection(String name) {
try {
Class<? extends Action> actionClass = Class.
forName("actpck."+ name + "Action").asSubclass(Action.class);
Action a = actionClass.newInstance();
a.doSomething();
} catch (Exception e) {
// TODO Catch exceptions individually and do something useful.
e.printStackTrace();
}
}
}
Each approach has it's trade offs:
HashMap = Fast + Kinda messy ("set-up" code with hundred of puts)
Enum = Fast + Kinda messy 2 (huge file).
Reflection = Slower + runtime error prone, but provides clean separation without resorting to clunky big HashMap.
Like Matt Ball said in his comment, you can use a command pattern. Define a collection of Runnable classes:
Runnable task1 = new Runnable() {
public void run() { /* do something */ }
};
Runnable task2 = // etc.
Then you can use a map from your keys to runnables:
Map<String,Runnable> taskMap = new HashMap<String,Runnable>();
taskMap.put("abc", task1);
taskMap.put("xyz", task2);
// etc.
Finally, replace the if-else chain with:
Runnable task = taskMap.get(name);
if (task != null) {
task.run();
} else {
// default else action from your original chain
}
you can use the switch statement , but Switch statements with String cases have been implemented in Java SE 7
the best solution is to use the command pattern
This is a popular Arrow Anti-Pattern and Jeff discusses some approaches to handle this very nicely in his post here.

Categories

Resources