I've got two enums: level
with 3 values and criticality
with 4 values. A combination of those two maps to one of 8 values from priority
enum. The mapping is non-linear and may change in future.
What is the best* way to implement a static function that takes level and criticality and outputs a priority?
*best being easy to read and understand, easy and safe to change, and not a performance hog. Extra points for a solution that takes into account that input domain can change in the future.
nested switch..case. Many lines and lots of boilerplate code. Also error-prone if you forget to return a value in a case. Basically the code looks like this:
switch (bc) {
case C1:
switch (el) {
case E1:
return EmergencyPriority.P1;
case E2:
return EmergencyPriority.P2;
case E3:
return EmergencyPriority.P3;
}
case C2:
switch (el) {
case E1:
return EmergencyPriority.P2;
case E2:
return EmergencyPriority.P3;
case E3:
return EmergencyPriority.P4;
}
case C3:
switch (el) {
case E1:
return EmergencyPriority.P4;
case E2:
return EmergencyPriority.P5;
case E3:
return EmergencyPriority.P6;
}
case C4:
switch (el) {
case E1:
return EmergencyPriority.P6;
case E2:
return EmergencyPriority.P7;
case E3:
return EmergencyPriority.P8;
}
}
Mutikey Map requires an external library and I haven't found a way to nicely insert initial values without many function calls and boilerplate composite keys.
if..else if.. else basically same as switch case but with more boilerplate code. Less error-prone though.
Two dimensional array when using the enum values as integers for array indices you risk failing silently if the positional enum values change.
Your solution here
This structure is probably the "best" way to store your data ("best" = what I'm assuming your after, because I'd be perfectly fine with your switch
based solution)
EnumMap<Level, EnumMap<Criticality, Priority>> map = new EnumMap<>(Level.class);
EnumMap<Criticality, Priority> c1 = new EnumMap<>(Criticality.class);
c1.put(Criticality.E1, Priority.P1);
..
map.put(Level.C1, c1);
...
Then, simply write this utility method to access the structure:
public static Priority priority(Level level, Criticality criticality) {
return map.get(level).get(criticality);
}
The advantage of EnumMap
is: It offers Map
convenience while being rather efficient, as all the possible keys are known in advance, so values can be stored in an Object[]
.
You've already mentioned this, but I'll still repeat the idea, because I've done this in the past, and with proper formatting (that must never be broken by devs, of course), this approach is very readable and not very error prone.
Remember, formatting is key here:
Priority[][] map = {
// Criticality.E1 Criticality.E2 Criticality.E3
// ----------------------------------------------------------------
/* Level.C1 */ { Priority.P1 , Priority.P2 , Priority.P3 },
/* Level.C2 */ { Priority.P2 , Priority.P3 , Priority.P4 },
/* Level.C3 */ { Priority.P3 , Priority.P4 , Priority.P5 },
/* Level.C4 */ { Priority.P4 , Priority.P5 , Priority.P6 }
};
And now, the method looks like this:
public static Priority priority(Level level, Criticality criticality) {
return map[level.ordinal()][criticality.ordinal()];
}
In order to prevent failing silently in case someone adds a new enum value in the middle, just add a unit test that asserts the expected ordinal for each enum literal. The same test can also assert the Level.values().length
and Criticality.values().length
values, and you're safe for the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With