Enum vs If-else - java

I have a requirement wherein I need to build an employee object as below from an event list. Currently I've written my code as below, but QE gave a comment saying possible use of enums instead of multiple if else's.
Can someone suggest me on how to achieve this with enums.
Employee e= new Employee();
for(Event event:events){
if("empid".equals(event.getName())
e.setEmployeeId(event.getvalue());
else if("empname".equals(event.getName())
e.setEmployeeName(event.getvalue());
else if("empsal".equals(event.getName())
e.setEmployeeSal(event.getvalue());
else if("empdob".equals(event.getName())
e.setEmployeeDOB(event.getvalue());
else if("emprole".equals(event.getName())
e.setEmployeeRole(event.getvalue());
}

If you are in control of development of Event, I believe what your QE saying is to replace event name by an enum (which is a sane design as you have already decide what is the possible types of event). However, if Event's design is out of your control, or you cannot have a child class of Event for your use (e.g. make an EmployeeEvent), then just ignore what I am going to say)
i.e.
enum EventType {
EMP_ID,
EMP_NAME,
....
}
interface Event {
EventType getType(); // instead of getName() which returns a String
}
Then your code can be simplified to
Employee e= new Employee();
for (Event event: events) {
switch (event.getType()) {
case EMP_ID:
e.setEmployeeId(event.getvalue());
break;
case EMP_NAME:
e.setEmployeeName(event.getvalue());
break;
....
}
}
You may even preset the action to do against each event type, using a map (which is of similar idea of another answer)
Map<EventType, BiConsumer<Employee, String>> eventActions = new EnumMap<>();
eventActions.put(EventType.EMPLOYEE_ID, Employee::setEmployeeID);
eventActions.put(EventType.EMPLOYEE_NAME, Employee::setEmployeeName);
so you can further simplify the above switch by:
Employee e= new Employee();
for (Event event: events) {
eventActions.get(event.getType()).accept(e, event.getValue()));
}

Create an enum with the below codes
public enum EMPLOYEE_FIELDS {EMPID, EMPNAME,EMPSAL, EMPDOB, EMPROLE};
EMPLOYEE_FIELDS empField = EMPLOYEE_FIELDS.valueOf(event.getName().toUpperCase());
switch(empField ){
case EMPID:
//do something here
break;
case EMPNAME:
//do something here
break;
// other cases.
// other cases.
// other cases.
default:
//do something here
}
Hope this helps!

I would suggest you move the logic of what to do with events into the enum. If you are using Java 8 then it would look something like:
enum EmployeeField {
ID("empid", Employee::setEmployeeID),
NAME("empname", Employee::setEmployeeName),
SALARY("empsalary", Employee::setEmployeeSalary),
...
private final String key;
private final BiConsumer<Employee, String> valueSetter;
EmployeeField(String key, BiConsumer<Employee, String> valueSetter) {
this.key = key;
this.valueSetter = valueSetter;
}
public void setEmployeeField(Employee employee, String value) {
valueSetter.accept(employee, value);
}
public static EmployeeField getFieldForKey(String key) {
return Arrays.stream(values[])
.filter(ef -> ef.key.equals(key))
.findAny()
.orElseThrow(new IllegalArgumentException("No employee field " + key));
}
}
Then you can dispense with the switch statement altogether and just use:
events.stream()
.forEach(ev -> EmployeeField.getFieldForKey(ev.getName())
.setEmployeeField(emp, ev.getValue()));
This also means that all the information about employee fields, including how to set employee values, is encapsulated in the enum and can be easily changed or extended without anything else being impacted.
Note that you can do a similar thing prior to Java 8 without using the lambdas but it's not as elegant (in my view) as the anonymous interface instances need to become explicit which makes the code a lot more complicated. Or you can override methods for each enum member which (in my view) is even uglier.

Related

Java - Alternative to very long switch case

In my Java "Pear" class, I have a huge list of approximately 1000 variables :
public class Pear {
private String
a100,
a110,
a120,
...
etc.
}
I need to set each one of these variables based on the given value of a banana, so I had a first basic idea to do it with a switch case :
public class Pear {
...
public void setValues(Banana aBanana) {
switch (aBanana.getValueName()) {
case "300886":
a100 = aBanana.getValue();
break;
case "309606":
a110 = aBanana.getValue();
break;
case "300843":
a120 = aBanana.getValue();
break;
/* ...and so on for 1000 variables*/
}
}
}
, but I feel like this is not the good way to accomplish this, and this is not going to be very readable neither maintainable. How could I replace this switch case ?
Edit : I think there is a misunderstanding on the call of "setValues". It is going to be called like this (I added some pears) :
public static void main(String[] bananas) {
Pear pear = new Pear();
pear.setValues(bananas[0]);
pear.setValues(bananas[1]);
pear.setValues(bananas[2]);
...etc for 200 times approximately...
}
Having hundreds of variables to store multiple values of the same kind is room for bugs and difficult maintenance (which led to this question).
If you changed your data structure, you would get rid of all the unnecessary variable declarations, and you would have logic coded against values (codes), rather than variable names.
A Map is designed to be used to associate keys to values (value names to values, in your case).
Map<String, String> valueMap = new HashMap<>();
public void setValues(Banana aBanana) {
valueMap.put(aBanana.getValueName(), aBanana.getValue());
}
Now this introduces changes elsewhere, but then that's justified because your typical "read" code would start from the same "value names" too:
public String getValue(String valueName) {
return this.valueMap.get(valueName);
}

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.

Figuring which design pattern to use?

In most radio devices, we can configure the wave which we want to explore and listen to stations using the demodulation mode compatible with this type.
There are at least two types AM and FM. We can model the radio device in this case as the following:
class RadioDevice {
void demodulate (String m) {
if(m.equals("FM")
/* FM modelation */
else if(m.equals("AM")
/* AM modelation */
}
}
How can I apply the strategy pattern in this case?
Why don't you use polymorphism ?
Make an interface:
interface Radio {
void modulate();
}
And than implement 2 classes:
FMRadio implements Radio{
public void demodule(){
//FM modulation
}
}
AMRadio implements Radio{
public void demodule(){
//AM modulation
}
}
And than, in your main, you could go:
Radio myRadio = new FMRadio();
myRadio.demodule();
If you can have an interface that covers the contract for both AM and FM demodulation, you could use the Strategy pattern:
Demodulator d; // interface Demodulator { byte[] demodulate(); }
switch(m) {
case "AM":
d = new AMDemodulator();
break;
case "FM"
d = new FMDemodulator();
break;
default:
throw new IllegalArgumentException("Unsupported type '"+ m + "'"); // you could use an Enum instead of a String
}
d.demodulate(waves);
This allows you to switch the Demodulator part on the fly while keeping the rest of the program logic in common (no duplication).
Check this repo (not mine) for design patterns and examples: https://github.com/iluwatar/java-design-patterns
To make it a proper Strategy pattern, I would add using a Context class to the previous answer of #Ladislav_M, that will wrap & encapsulate executing a particular strategy and give more flexibility to the code:
class Context {
private Radio radio;
public Context(Radio radio) {
this.radio = radio;
}
public Object runStrategy() {
radio.demodulate();
// do any other stuff you want
return ...
}
}
The execution in main would become more convenient:
Context context = new Context(new FmRadio());
Object result = context.runStrategy();
Or you can inline the above:
Object result = (new Context(new FmRadio())).runStrategy();
Of course, you can choose the implementation of Radio in a switch block and just pass it as a variable to the Context's constructor.
This is not a good use case for Strategy design pattern, its simple inheritance case. Strategy is used where the state of the object does not change but different algorithms apply at different times. e.g. Paypackage computation for different roles of employees (e.g. Temporary, Permanent etc.). Important point here is Temporary employee can become Permanent one day.
In the above case AM will never become FM in its life time. hence Strategy is not right pattern for it. These are (probably) different classes with common behavior (if present) can be shifted to base class. If they show a common contract with clients then even interface will do the task.

Reduce if else statement in my ussd application

I have a ussd application where I generate an interface and with predefined options e.g 1. my account 2. transactions 3. bill enquiry. the user keys in either 1 or 2 or 3 or any other predefined option on their handset. now since the input from all interfaces is the same values, to keep track of the user's progress i have states i set each time a user navigates to a certain interface. now my problem is the states are becoming too many I have about 30 states and the if else statement is starting to look like one big ball of spaghetti plus not forgetting this approach is not scalable. any one can help me do a better design probably one that's scalable.
if (state == 35) {//exit application
a = mm.exit(uid);
out.println(a);
} else if (state == 3) {
a = view.main_menu_nav(uid, value.trim());
out.println(a);
} else if (state == 4) {
a = view.my_account(uid);
out.println(a);
} else if (state == 5) {
a = view.my_account_nav(uid, value.trim());
out.println(a);
} else if (state == 6) {
String value = USSD_STRING;
a = view.transaction_nav(uid, value.trim());
out.println(a);
} else if (state == 7) {
a = view.deactivate_nav(uid, value.trim());
out.println(a);
}
In your example snippet, there's repetition of out.println(a) in each clause of the if, you can simplify the code by moving out.println(a) outside of the if. You could also use a switch instead of an if.
Neither of these ideas fundamentally improve the design, however.
I would suggest you look at the "State" design pattern. Essentially, you have a separate object for each state that knows how to handle interactions in that state, and an outer container that receives events, holds a reference the current state object, and delegates the events to it.
A second (orthogonal) suggestion is to name your methods to reveal intentions—i.e. behavior. The method deactivate_nav() is OK, but my_account(uid) does not say what the method does.
It's hard to give specific advice without more information on your specific problem.
You can use a Map and let the key be the state, the value is an object of an interface, e.g. StateAction which provides a proper method which you then call:
StateAction action = stateActions.get(state);
action.execute();
The interface would be defined as
interface StateAction
{
void execute();
}
The map can be filled programmatic:
stateActions.put(35, new StateAction
{
public void execute()
{
//exit application
YourType a = mm.exit(uid);
out.println(a);
}
});
stateActions.put(3, new StateAction
{
public void execute()
{
YourType a = view.main_menu_nav(uid, value.trim());
out.println(a);
}
});
// and so on ...
It's also possible to create a class for your action and let it implement the interface:
class AccountHandler implements StateAction
{
// ...
public void execute()
{
YourType a = view.my_account(uid);
out.println(a);
}
// ...
}
Add this with
stateActions.put(4, new AccountHandler());

Instantiate class dynamically based on some constant in Java

I am making a multiplayer game which makes heavy use of a serialisable Event class to send messages over a network. I want to be able to reconstruct the appropriate subclass of Event based on a constant.
So far I have opted for the following solution:
public class EventFactory {
public static Event getEvent(int eventId, ByteBuffer buf) {
switch (eventId){
case Event.ID_A:
return EventA.deserialise(buf);
case Event.ID_B:
return EventB.deserialise(buf);
case Event.ID_C:
return EventC.deserialise(buf);
default:
// Unknown Event ID
return null;
}
}
}
However, this strikes me as being very verbose and involves adding a new 'case' statement every time I create a new Event type.
I am aware of 2 other ways of accomplishing this, but neither seems better*:
Create a mapping of constants -> Event subclasses, and use clazz.newInstance() to instantiate them (using an empty constructor), followed by clazz.initialiase(buf) to supply the necessary parameters.
Create a mapping of constants -> Event subclasses, and use reflection to find and call the right method in the appropriate class.
Is there a better approach than the one I am using? Am I perhaps unwise to disregard the alternatives mentioned above?
*NOTE: in this case better means simpler / cleaner but without compromising too much on speed.
You can just use a HashMap<Integer,Event> to get the correct Event for the eventID. Adding or removing events is going to be easy, and as the code grows this is easy to maintain when compared to switch case solution and speed wise also this should be faster than switch case solution.
static
{
HashMap<Integer,Event> eventHandlerMap = new HashMap<>();
eventHandlerMap.put(eventId_A, new EventHandlerA());
eventHandlerMap.put(eventId_B, new EventHandlerB());
............
}
Instead of your switch statement Now you can just use :
Event event = eventHandlerMap.get(eventId);
if(event!=null){
event.deserialise(buf);
}
If you're not afraid of reflection, you could use:
private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
static {
try {
for (Field field : Event.class.getFields())
if (field.getName().startsWith("ID_")) {
String classSuffix = field.getName().substring(3);
Class<?> cls = Class.forName("Event" + classSuffix);
Method method = cls.getMethod("deserialize", ByteBuffer.class);
EVENTID_METHOD_MAP.put(field.getInt(null), method);
}
} catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Event getEvent(int eventId, ByteBuffer buf)
throws InvocationTargetException, IllegalAccessException {
return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
}
This solution requires that int ID_N always maps to class EventN, where N can be any String where all characters return true for the method java.lang.Character.isJavaIdentifierPart(c). Also, class EventN must define a static method called deserialize with one ByteBuffer argument that returns an Event.
You could also check if field is static before trying to get its field value. I just forget how to do that at the moment.

Categories

Resources