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.
Related
I'm creating a springboot banking API and in order to create a transaction a bunch of "rules" have to be checked.
e.g:
Current logged in user can't withdraw money from another user's savings account
Amount can't be higher/lower than certain number
etc.
This causes my createTransaction method to contain a lot of if statements (12!). This is what my code looks like in pseudo:
public ResponseEntity<String> createTransaction(Transaction body) {
if (check rule 1) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("...");
}
if (check rule 2) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("...");
}
// etc...
// Transaction complies to set rules
return ResponseEntity.status(HttpStatus.CREATED).body("Transaction successful!");
}
I can post my actual code if necessary but I think this paints the picture without having anyone to read 100 lines of code.
Because I have around 12 if statements checking these rules, my function is quite lengthy and difficult to read/maintain.
Googling for a solution didn't bring up results I was looking for. I've tried implementing exceptions but this didn't remove the amount of if statements. Maybe a switch could improve a bit, but I'm wondering if there's a clean OOP solution.
My question is: How can I clean this code up (OOP style)?
Thanks in advance.
You should create a TransactionRule interface that allows you to implement specific transaction rules, and then use a stream to get the final result:
public interface TransactionRule {
public boolean isAllowed(Transaction someTransaction);
}
Example implementation 1:
public class SufficientBudgetTransactionRule implements TransactionRule {
public boolean isAllowed(Transaction someTransaction) {
// Custom logic e.g.
return someTransaction.wallet.value >= someTransaction.transaction.value;
}
}
Example implementation 2:
public class NotInFutureTransactionRule implements TransactionRule {
public boolean isAllowed(Transaction someTransaction) {
// Custom logic e.g.
return someTransaction.transaction.datetime.isBefore(OffsetDateTime.now());
}
}
Then, you can store all the TransactionRules in a List and check whether they all validate like so:
private final List<TransactionRule> transactionRules; // Fill these of course
public boolean allTransactionRulesMatch(Transaction someTransaction) {
return transactionRules.stream()
.map(transactionRule -> transactionRule.isAllowed(someTransaction))
.allMatch(result => result);
}
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.
We have some code like:
public class ErrorCodeUtil {
public static void handleErrorCode(String errorCode) {
if (errorCode.equals("1")) {
handleErrorCode1();
} else if (errorCode.equals("2")) {
handleErrorCode2();
} else if (errorCode.equals("3")) {
handleErrorCode3();
} else {
handleErrorCodeByDefault(errorCode);
}
}
public static void logByErrorCode(String errorCode) {
if (errorCode.equals("1")) {
logErrorCode1();
} else if (errorCode.equals("2")) {
logErrorCode2();
} else if (errorCode.equals("3")) {
logErrorCode3();
} else {
logErrorCodeByDefault(errorCode);
}
}
//... a lot of method about error code
}
As you see, we have a Util to handle all things about ErrorCode, and when we want to add a special logic to an error code, we have to change many method of that utils class.
As expected, the value of error code varies in large range(possibly "112345" or "error_code_001"). So what design pattern is proper for that case?
I would implement a decision table.
The table would consist of a set of mappings between one or more Predicates as key and Function as a value. If a Predicate condition is met, then the corresponding Function is executed. If no Predicate condition is met, then a default Function should be executed. This can (easily) replace the humongous "if-else" statement and should be easier for maintenance.
How a Predicate should look like? It should take a String (in your case) and should return a boolean indicating whether a condition is met or no:
interface Predicate {
public boolean test(String x);
}
In the decision table, you'd add (anonymous) implementations of this interface as keys.
Hint: If you are already on Java8, even better, there's a built-in Predicate<T> interface. But if you're not, then you can introduce a Predicate interface of your own. :-)
The Function for the decision table's values will be a similar interface. It may (or may not) use an input parameters and should return void. In Java8 this is called a Consumer, however in my example I'll stick to the Function naming:
interface Function<T> {
void apply(T t);
}
By constructing pairs between Predicate as a key and Function<ErrorCodeUtil> as a value, we'll populate the decision table. When a Predicate condition is met, then we'll invoke the corresponding Function's .apply() method:
The decision table itself can be a simple Map<Predicate, Function<ErrorCodeUtil>>:
Map<Predicate, Function<ErrorCodeUtil>> decisionTable = new HashMap<>();
and you should populate it at construction time or whenever you wish (just before the handleErrorCode() method logic):
Predicate equalsOne = new Predicate() {
public void test(String x) {
return "1".equals(x);
}
};
Function<ErrorCodeUtil> actionOne = new Function<ErrorCodeUtil>() {
public void apply(ErrorCodeUtil t) {
t.handleErrorCode1();
}
}
decisionTable.put(equalsOne, actionOne);
and so for the other "condition-action" pairs, including the default action (i.e. the last else statement) for which the Predicate will always return true.
Note that in Java8, those anonymous classes can be significantly reduced by just using lambdas.
Finally, your "if-elseif" statements would be re-factored to a simple loop:
for (Map.Entry<Predicate, Function<ErrorCodeUtil>> entry: decisionTable.entrySet()){
Predicate condition = entry.getKey();
Function<ErrorCodeUtil> action = entry.getValue();
if (condition.test(errorCode)) {
action.apply(this);
}
}
So, everytime you add a new condition, you won't have to touch the handleErrorCode(String error) method, but you'll have to just introduce a new (anonymous) implementation of Predicate and Function and .put() it into the decision table.
I'd use Enum in that case.
public enum ErrorCodeEnum {
1 {
#Override
public void handleErrorCode() {
//doSomething
}
},
2 {
#Override
public void handleErrorCode() {
//doSomething
}
};
public abstract void handleErrorCode();
}
Then, having the error code in hands...
ErrorCodeEnum.valueOf("1").handleErrorCode();
PS: This is what I'd use to replace if-else statement, as you asked. But I'd use a Logger API for that specific problem (seems like you're logging erros).
You can keep all errorcodes in a list in one class. And check if list contains errorcode or not.
So this will reduce your if...else logic.
You have written different methods to handle error codes like handleErrorCode1(), handleErrorCode2() etc. Now if list contains desired error code then you can invoke these methods through java reflection.
regarding logging of errors, if all that is required is matching a code with a message, then a text file with mapping of codes to messages is the right way. the text file may be properties:
1=Item not Found
2=Item not valid
that can be loaded to a java.util.Properties instance, it may be xml that can be loaded into DOM or HashMap
<errors>
<error>
<code>1</code>
<msg>Item not Found</msg>
</error>
<error>
<code>2</code>
<msg>Item not Valid</msg>
</error>
<errors>
one advantage of this approach is that it can be made to support i18n if you specify language code in the file name and then get user language code from your client
I have a requirement to index items. This service should run Sync or Async.
I started designing an Interface
public interface IndexService{
public void index();
}
And two implementation, one for a Async Index:
public class AsyncIndex implements IndexService {
public void index(){
//... Creates a Thread and index the items
}
}
And the other one to the Sync Index
public class SyncIndex implements IndexService {
public void index(){
//... Creates a Thread and index the items
}
}
But now there is another design that is having a IndexService, who has a flag to execute as a async service or as a sync service:
public interface IndexService{
public void index(int mode);
}
So now the implementation will know how to run base on that flag.
I know that the first design is better, but I need pros and cons to explain why.
I go for first approach because
1- code is cleaner AsyncInex class only has codes related to async call and syncIndex would has its own code.
2- you can avoid else if
...
public void runService(IndexService service) {
service.index()
}
// some where in your code
runService(new AsyncIndex());
// or
runService(new SyncIndex());
as you are working with interface "IndexService" you can always change implementation without changing clients code.
specially if you are using DI frameworks you can have the kick of it ;).
this is so important to not allowing client code know about the implementation. suppose situation where you are indexing, for instance, a database.
you want to do async index when data is huge or sync index when data is small.
caller should has no knowledge about the way Index is called. this way you can have different strategy in different situations without changing callers code. if you take the second approach you have to do some extra work.
I say both.
Assume, you plan to use the second approach. Your implmentation may look like:
public SyncOrAsyncIndex implements IndexService {
public void index(int mode) {
if(mode == 0) {
//sync processing code
} else if (mode == 1) {
//async procesisng code
}
}
That said, are you going to write all the implementation within this index method or SyncOrAsyncIndex class. That will possibly end up being unmanageable.
So, the index method may end up like this:
public void index(int mode) {
if(mode == 0) {
new SyncIndex().index(); //for example
} else if (mode == ) {
new AsyncIndex().index(); //for example
}
}
Assume, you decide on supporting a third mode. Imagine the plight of the index method or SyncOrAsyncIndex class. So, the first approach is needed.
So, as per "code to the interface" strategy the first approach is suggested. If the invoker is aware of the type of indexing, they can just instantiate the particular type and use it.
Else, along with the first approach the second one may be required as a factory or strategy to calculate which type of indexing to use based on the passed parameter. The invoker would then use the SyncIndex or AsyncIndex via SyncOrAsyncIndex.
I want to make a simple interative shell based on the console where I can write commands like login, help, et cetera.
I first thought of using Enums, but then I didn't know how to implement them neatly without a load of if-else statements, so I decided to go with an array-approach and came up with this:
public class Parser {
private static String[] opts = new String[] {"opt0", "opt1", "opt2", "opt3" ... }
public void parse(String text) {
for(int i = 0; i < opts.length; i++) {
if(text.matches(opts[i]) {
switch(i) {
case 0:
// Do something
case 1:
// Do something-something
case 2:
// Do something else
}
return;
}
}
}
}
But I ended up seeing that this was probably the most rudimentary way of doing something like this, and that there would be problems if I wanted to change the order of the options. How could I make a simpler parser? This way it would work, but it would also have said problems. The use of the program is purely educational, not intended for any serious thing.
A simple approach is to have a HashMap with the key equal to the command text and the value is an instance of class that handle this command. Assuming that the command handler class does not take arguments (but you can easily extend this) you can just use a Runnable instance.
Example code:
Runnable helpHandler = new Runnable() {
public void run(){
// handle the command
}
}
// Define all your command handlers
HashMap<String, Runnable> commandsMap = new HashMap<>(); // Java 7 syntax
commandsMap.put("help",helpHandler);
// Add all your command handlers instances
String cmd; // read the user input
Runnable handler;
if((handler = commandsMap.get(cmd)) != null) {
handler.run();
}
You can easily extend this approach to accept argument by implementing your own interface and subclass it. It is good to use variable arguments if you know the data type e.g. void execute(String ... args)
One solution that comes to mind is actually using Design patterns. You could use the input from the user, as the discriminator for a Factory class.
This factory class will generate an object, with an "execute" method, based on the input. This is called a Command object.
Then you can simply call the method of the object returned from the factory.
No need for a switch statement. If the object is null, then you know the user entered an invalid option, and it abstracts the decision logic away from your input parser.
Hopefully this will help :)