Shorten lines of code in case statements inside switch block - java

I have 10 cases in my switch statement. Each of them does the same thing with the other except that the assignments are different. If I have to change 1 line of code (during development) in one case block then I have to change all other 9 cases manually.
Here are the scenarios of each case statements:
Each case contains long lines of code with many function calls and assignments.
Only variable assignments, function arguments, and if statement conditions vary.
There is no pattern/sequence in the assignment of variables and function arguments.
Adding helper functions and calling them on each case statements is almost impossible for some reason.
How do I optimize or shorten this?
To illustrate:
final int CONSTANT_A = 0;
final int CONSTANT_B = 1;
...
final int CONSTANT_J = 10;
int varA = 0;
int varB = 1;
...
int varJ = 10;
int anothervarA = 0;
int anothervarB = 1;
...
int anothervarJ = 10;
int action = 0;
switch(something) {
case 1:
... long lines of code here
// If I have to change the variables below
// then I have to update all other variables in
// other cases below
varA = CONSTANT_J;
anothervarA = CONSTANT_B;
... another long lines of code here
int ret = someObject.foo(varA);
... do something with ret.
action = 5;
break;
case 2:
... long lines of code here
varB = CONSTANT_I;
anothervarB = CONSTANT_C
... another long lines of code here
int ret = someObject.foo(varA);
... do something with ret.
action = 100;
break;
...
...
case 9:
... long lines of code here
varI = CONSTANT_B;
anothervarI = CONSTANT_A;
... another long lines of code here
int ret = someObject.foo(varA);
... do something with ret.
action = 100;
break;
case 10:
... long lines of code here
varK = CONSTANT_A;
anothervarJ = CONSTANT_F;
... another long lines of code here
int ret = someObject.foo(varA);
... do something with ret.
action = 4;
break;
}

Nothing obvious jumps out, except maybe to factor all that code into a set of classes (or an enum), and rely on polymorphism instead of switch to call the right one. So for instance, load your cases into a Map<Integer,Foo> -- or even a List<Foo> if it's not a sparse array -- and then replace the switch with myFoos.get(something).whatever();.
As for assigning the variables when you're done, if they're member variables you could have the Foos set them directly; if this is always called in a single-threaded environment, you could have foo.whatever() set up state and then have getters. If it's in a multi-threaded environment, you could have whatever() return back a new object with those getters. Something like:
FooResult result = myFoos().get(something).getResult(whatever, args);
varA = result.getA();
action = result.getAction();
etc

Given your criteria, I don't think there is much you can do. If there is no pattern in what you are calling or assigning then it's going to be pretty hard to optimize this. It looks like there is some code that is common that could be pulled out into helper methods but then you say:
Adding helper functions and calling them on each case statements is
almost impossible for some reason.
I'm not really sure what this means but if you cannot create helper methods for some reason, I don't think there is anything you can do.

You could put the shared code outside the switch statement, and separate the switch statement into multiple switches so the shared code can go in between:
// declare & initialize variables
//... long lines of code here
// first switch: only assign values
switch(something) {
case 1:
varA = CONSTANT_J;
anothervarA = CONSTANT_B;
break;
// more cases...
}
//... another long lines of code here
// second switch: only call the method someObject.foo(???);
switch(something) {
case 1:
int ret = someObject.foo(varA);
break;
// more cases...
}
//... do something with ret.
// third switch: assign the action value
switch(something) {
case 1:
action = 5;
break;
// more cases...
}
This lets you write the repeated code only once at the cost of having multiple switch statements, which add lots of extra case and break lines.
Depending on the situation, you might be able to use arrays to completely get rid of the switch statements. For example, you could make an array of all the action values and then assign action the element at index something - 1:
int[] actionValues = { 5, 100, \*...,*\ 100, 4};
action = acionValues[something - 1];
This could also be applied to the variable initialization, although it would be tricky. You'd have to store all the variables and constants in arrays, and either find a mathematical pattern or hardcode a set of rules for each case (each rule contains the index of a constant and the index of a variable to assign it to). To still access the variables and constants by their name (as opposed to index), you could make getters and setters:
int getVarA() { return varArray[0]; }
void setVarA(int val) { varArray[0] = val; }
int getVarB() { return varArray[1]; }
void setVarB(int val) { varArray[1] = val; }
int CONSTANT_A() { return constArray[0]; } // no need for a setter
For the first case, the rules might be
assign constant at index 9 (CONSTANT_J) to variable at index 0 (varA)
assign constant at index 1 (CONSTANT_B) to variable at index 26 (anothervarA)
You can store these rules in an array, and store the rule-arrays for each case in an array of arrays:
int[][] rules = {
/* rules for case 1 */
{ 9, 0, 1, 26 }, /* interpret as: const9 -> var0, const1 -> var26 */
/* rules for other cases */
};
To 'execute' the rules for the case:
int c = something - 1; // give the case a short name to save typing
varArray[ rules[c][1] ] = constArray[ rules[c][0] ];
varArray[ rules[c][3] ] = constArray[ rules[c][2] ];

Related

How do I make a get method return a decreasing/increasing value for every time it is called?

I have a unit(soldier) class which contains a getAttackBonus() method and a getResistBonus() method. These must return different values for every time a soldier either attacks or is attacked.
To be specific, getResistBonus() can for example start at 8 but for every time the soldier is attacked, it will decrease by 2 until it reaches a certain value (for example 2 as a final resist bonus) where it will no longer decrease. How would I go about doing this?
Currently I am using in my method which does not work when I attempt to test it as a JUnit class, it keeps giving me 6 as the integer:
public int getResistBonus() {
int resist = 8;
while(resist != 2) {
return resist -= 2;
}
return 2;
}
You need to change a bit your code.
First you need to define resist at instance level, not method level.
Second it is better to use an if instead of a while because you are not making a loop, but only checking a single condition.
So the code can be something similar to that:
public class YourClass {
// Define resist at instance level
private int resist = 8;
....
public int getResistBonus() {
// Replace the while with an if
if (resist > 2) {
resist -= 2;
}
return resist;
}
}

how to get only one key in a HashMap with "similar" keys

I have an object "ObjectName" defined by String hostName and List serviceList.
two serviceLists might contain one or more of the same string.
Each string is the name of a method. There can't be methods with same name that do different things or methods with different names that do the same things.
Each ObjectName is paired with an unique integer. Then I create a
Map<ObjectName, Integer> objectPorts = new HashMap<>();
I add to this map two nodes with a duplicate string in their serviceList
example:
objectName1's serviceList has {method1, method2, method3}, while objectName2's serviceList has {method4, method2, method5}
objectPorts contains two times the string "method2"
Now I want to search the map for "method2" and execute the code of method2. I want the execution of the code to be done only once, not for each entry of "method2".
how to do this?
I was using this code: (edit: added the switch portion for clarification)
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result = 0;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
switch(methodName) {
case "method1":
result = arithmeticClient.method1(n1, n2);
break;
case "method2":
result = arithmeticClient.method2(n1, n2);
break;
case "method3":
result = arithmeticClient.method3(n1, n2);
break;
case "method4"
result = arithmeticClient.method4(n1, n2);
break;
}
}
return result;
}
but it executes the code of methodName for each instance of methodName present in the map, so it's not what i want it to do. how can I modify it?
From what I am understanding, all you would need to do is break out of the for loop once it has executed. So something like this --
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
does something
break;
}
}
break is actually a java keyword that "breaks" out of the current loop you are in. So adding it after the code "does something" would mean the loop would exit and the code would only be executed the one time.
The simple way to fix this would be just to add a boolean to indicate if method 2 had been called previously during the loop:
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result 0;
boolean executed = false;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) && !executed){
//do something
executed = true;
}
}
return result;
}
This will stop method 2 or whatever from being executed more than one time if the value is contained in multiple Lists associated with the objects of your map.

Returning the needed value in a class - Java

I am confused about returning the needed value. Here is a part of my code:
public class StrNum {
public static int getInt(String input) {
String str = new String(input);
int result;
if (str.startsWith("b")) {
str = str.substring(1);
result = Integer.parseInt(str, 2);
}
else if (str.startsWith("x")) {
str = str.substring(1);
result = Integer.parseInt(str, 16);
}
Now, what I need to return is result. When I write return result;, it asks me to initialize the variable (and I am aware that it hasn't been initialized). When I use return result inside of the if statements, Eclipse tells me that I have to return a value.
Where am I being stupid here? I would appreciate a good explanation.
What will the method getInt() return if Str doesn't start with neither "b" or "x"? That would be an error because result is not being initialized. You could solve this by intializing result with a value that you would like to return in that case:
int result = -1; // for example
Edit:
Since you want to use input to determine if the number will be parsed as binary or hexadecimal, I would recommend you to add an else statement to parse the number in base 10 as default:
if (...)
// ...
else if (...)
// ...
else
result = Integer.parseInt(Str);
Note:
Try to follow Java naming conventions. Use names like someVar for variables/methods and use names like SomeClass for classes.
It's not necessary to create a new string instance Str, unless you are going to use the original input later in the same method.
To create a String with the same content you can simply do
String str = input;
Try
int result = 0;
You must initialize variables before returning them or in other words do result = . The variable must always be initialized no matter what code path your application takes.
Your can either return the result from if and also from else, provided you are not doing additional calculations after the else block.
or simply initialize result = 0, it will change anyways before you return.

How to combine two methods with identical calculates but different fields

How do I combine two methods that have identical calculations but operate (read and write) different fields of the class.
A VERY simplified aircode example:
class TileCalculator
{
int length;
int width;
int tileLength;
int tileWidth
int cols;
int rows;
void calculateColumns()
{
this.cols = this.width/this.tileWidth;
}
void calculateRows()
{
this.rows = this.length/this.tileLength;
}
}
As these two methods do exactly the same calculation(s) but just using different fields for their input and output it would seem sensible to combine them but I don't know how.
UPDATE: I think I may have oversimplified it to the point where answerers are trying to solve the specific case. A more realistic example is:
void calculateCols()
{
int tileCols = width/tileWidth;
int remainder = width%tileWidth;
if (remainder==0) {
// there is an exact number of whole tiles
fullTileCols = tileCols;
firstT = tileWidth;
usedTileCols = tileCols;
} else {
// there is a remainder
fullTileCols = tileCols - 1;
firstT = (remainder+tileWidth)/2;
usedTileCols = tileCols + 1;
}
}
void calculateRows()
{
int tileRows = length/tileLength;
int remainder = length%tileLength;
if (remainder==0) {
// there is an exact number of whole tiles
fullTileRows = tileRows;
firstCut = tileLength;
usedTileRows = tileRows;
} else {
// there is a remainder
fullTileRows = tileRows - 1;
firstCut = (remainder+tileLength)/2;
usedTileRows = tileRows + 1;
}
}
I'm not saying a redesign isn't the answer but as you can see there are multiple fields involved so a simple return value probably isn't going to cut it. This is why I am using fields rather than a simple function and the maintainability of the current setup is of concern to me.
No, I wouldn't combine them, I would change them.
I'd get rid of rows and cols fields
I'd get rid of the above methods as it makes your object's state dependent on these methods always being called before an object is used -- a risky proposition.
Instead I'd create two calculated getter methods. This way the calculations are guaranteed to be done when needed.
e.g.,
public int getColumns() {
return width / tileWidth;
}
public int getRows() {
return length / tileLength;
}
Edit
I suppose you could create a RowCol class that has full, first, and used fields, and that has but one equation for doing the calculation above, and that you create two instances, one for row and one for column in the containing class, but if the rationale for this is to just combine these small methods, I question the need for this, or the benefit. Yes, you should follow the DNRY rule, but I worry more about this when I have three or more repeats of the same code.
You could make a convenience method. In the case you have shown this is actually more typing, longer program, extra complexity etc for no benefit. But if the calculation was more complicated it could be worth it
int calculate(int a, int b)
{
return a/b;
}
void calculateColumns()
{
this.cols = this.calculate(this.width, this.tileWidth);
}
after the update
you actually want 3 return values(full, first, used) so alter the "calculate" to either return a special class with 3 int or an array of int
Then feed in a and b as before but with the adjusted logic and return the 3 values and set them in the calling function
There is no easy way to do this in Java prior to Java 8. You can do it, but it involves using private internal interfaces and anonymous classes. It isn't worth it unless you're really talking about a lot of common lines of code.
With Java 8 though, you'll be able to use closures which will greatly simplify this kind of cases.

Calling random functions

public static GetRandomFunc() {
switch((int)(Math.random()*NUM_FUNCTIONS) {
case 0:
functionA();
break;
case 1:
functionB();
break;
case 2:
functionC();
break;
// ...
}
}
I want to call GetRandomFunc() in main randomly until each function has been called once and then it ends. How do I make sure a function would be called once only, and not called again.
It would be easier to store values in an collection, and draw them out randomly until the collection is empty. Or better yet, shuffle the collection and then walk over it sequentially.
The values in the collection could be integers, or they could be objects of different classes (with a common superclass or interface), which provide different implementations of a call() method.
For example:
import java.util.*;
import java.util.concurrent.*;
List<Runnable> functions = new ArrayList<Runnable>();
functions.add(new Runnable() {
public void run() { /* do something */ }
});
functions.add(new Runnable() {
public void run() { /* do something else */ }
});
Collections.shuffle(functions);
for(Runnable function : functions)
function.run();
The other posts on this thread show other potential solutions, but all of them are more complex and error-prone than this one, and most of them would be very slow if the number of functions is large. (The one from #AndersLinden is an exception -- it would still be fast even if there are thousands of functions to call.)
Bit fields to record whether a function has been called and an if statement to not call the function if it's bit is already set - keep looping until all of the bits are set.
(or as Alex D says, create a collection of numbers to use up front)
Either way the trick is to make sure you generate each number once and once only - if you screw this bit up you can end up in an infinite loop (e.g. waiting to get numbers 1, 2 and 3, but your random function is generating 0, 1 and 2)
You could create an array to keep track of which functions have already been used.
For example:
boolean[] usedFunctions = new boolean[NUM_FUNCTIONS];
public static GetRandomFunc() {
switch((int) (Math.random() * NUM_FUNCTIONS) {
case 0:
if(!usedFunctions[0]) {
functionA();
usedFunctions[0] = true;
}
break;
case 1:
if(!usedFunctions[1]) {
functionB();
usedFunctions[1] = true;
}
break;
// etc.
}
}
Then all you need to do is repeatedly call GetRandomFunc() until all elements in usedFunctions are true.
You keep an array of integers that tells which indexes that are still not used.
When you have used an integer, you fill it in the hole with the last index in the list and treat it as a list with one item shorter.
int indexes[] = new int[3];
for (int i = 0; i < 3; i++)
indexes[i] = i;
for (int i = 0; i < 3; i++)
{
int index = (int)(Math.random()*(3 - i));
switch (indexes[index])
{
case 0:
functionA();
break;
case 1:
functionB();
break;
case 2:
functionC();
break;
}
indexes[index] = indexes[2 - i];
}

Categories