How to handle thousands of error message constant in java? - java

My app is sending request to APIs from some other parties.
Every single parties have different return codes that I need to handle.
The result is I need to handle thousands of different return code from those parties.
I am thinking about making a specific class that will hold constant to handle all of them.
It will be like:
public static final Map<String, String> RETURN_CODE_MAP = new HashMap<String, String>(){
{
// these mapping will be thousands of line
put("BANK_A_000","SUCCESS");
put("BANK_A_001","FAILED");
put("BANK_A_002","UNKNOWN");
put("BANK_B_SU","SUCCESS");
put("BANK_B_FA","FAILED");
put("BANK_B_UN","UNKNOWN");
put("BANK_C_00077","SUCCESS");
put("BANK_C_00088","FAILED");
put("BANK_C_00099","UNKNOWN");
put("E-COMMERCE_A_000","SUCCESS");
put("E-COMMERCE_A_001","FAILED");
put("E-COMMERCE_A_002","UNKNOWN");
put("E-COMMERCE_B_000SU","SUCCESS");
put("E-COMMERCE_B_000FA","FAILED");
put("E-COMMERCE_B_000UN","UNKNOWN");
put("E-COMMERCE_C_00077","SUCCESS");
put("E-COMMERCE_C_00088","FAILED");
put("E-COMMERCE_C_00099","UNKNOWN");
}
};
The list of the return code will be thousands of them.
Are there going to be a performance issue?
Can you guys tell me the right way to handle this kind of case? Thank you all

I would suggest using an Enum per party. It allows you to store additional information easier than a map and if used right saves you some performance that could get wasted by string comparison (for example map.get(x).equals("SUCCESS")).
This could look like this:
ResponseStatus.java
public enum ResponseStatus {
SUCCESS,
FAILED,
UNKNOWN;
}
BankA.java
import static my.pkg.ResponseStatus;
public enum BankA {
C_000("000", SUCCESS),
C_001("001", FAILED),
C_002("002", UNKNOWN),
// And so on ...
private final ResponseStatus status;
private final int hashCode;
private BankA(String code, ResponseStatus status) {
this.status = status;
this.hashCode = code.hashCode();
}
public ResponseStatus getStatus() {
return this.status;
}
public static BankA byStaus(String status) {
BankA[] values = values();
int hash = status.hashCode();
for (int n = 0; n < values.length; n++) {
BankA value = values[n];
if (value.hashCode == hash) return value;
}
return null; // No entry found by that code
}
}
Note, that in byStatus I'm comparing the hash codes instead of the strings themselves which is faster than comparing hundreds of strings.
In the end your final check could look like this:
BankA status = BankA.byStatus(response);
if (status != null && status.getStatus() == ResponseStatus.SUCCESS) {
// Do something
}

Related

What is better to use just one method with switch or 4 different methods?

I have a object and i get some fields from it, i made a method with a switch statement, the idea was make it generic and just call this method through parameters but now I´m not sure.
The options are
private String getCode(Row row, String code) {
String result;
switch (code) {
case code1:
result = row.getString("constant1");
break;
case code2:
result = row.getString(constant2);
break;
case code3:
result = row.getString(constant3);
break;
case code4:
result = row.getString(constant4);
break;
default:
result = null;
}
return result;
}
or
private String getcode1(Row row){
return row.getString("constant1")
}
private String getcode1(Row row){
return row.getString("constant2")
}
private String getcode1(Row row){
return row.getString("constant3")
}
private String getcode1(Row row){
return row.getString("constant4")
}
I wand to use the better way, I´m a little confuse
The answer is neither of the them.
What you have here is a value conversion process. How this works? Well, as far I understand, you need to store the data in form of key-value pair where key must be unique. So this is the definition of a HashMap in java.
Also, because you are using Switch I'm assuming that you can identify your data with some unique key. This meas for each code there is only one constant. But how we can apply this to my issue? Well, I think you have a collection of data from where you extracted a single row. Now, from this row you want to access to a value (lets call it codeValue) using a constant, but to get this constant you need a code. Like this: code->constant->codeValue
How can I implement this??
Well, we gonna need a HasMap() called constants, which defines its keys a codes and the values as constants. Now you easily get each constant if you know its code. Obviously, if you get the constant you can also get the codeValue of each `row, like this:
public class TestClass {
public static void main(String[] args) {
// Create a HashMap object called constants
Map<String, String> constants = new HashMap<String, String>();
// Add keys and values (code, constant)
constants.put("code1", "constant1");
constants.put("code2", "constant2");
constants.put("code3", "constant3");
constants.put("code4", "constant4");
System.out.println(constants);
Row row = loadRow();
String code = loadCode();
//How to use it
String result = getCodeValue(row, code);
System.out.println(result);
}
// Now you get the code from the Map
private String getCodeValue(Row row, String code) {
return row.getString(constants.get(code));
}
}
In case your input code is different than constant, I will suggest the following approach. Create an Enum mapping code and constant.
public enum Mapping {
MAPPING_FIRST("code1", "constant1"),
MAPPING_SECOND("code2", "constant2");
private String code;
private String constant;
// constructor and getters
public static Mapping getMappingFromCode(String code){
return Arrays.stream(Mapping.values())
.filter(mapping -> mapping.getCode().equals(code))
.findFirst()
.orElse(null);
}
}
Now, create a method to access value from row.
private String getValue(Row row, String code) {
Mapping mapping = Mapping.getMappingFromCode(code);
if(mapping == null){
return null;
}
return row.getString(mapping.getConstant());
}
This question is little bit to much, but I will try to explain best I could. For me second option is no no at all. Why? You are making methods that you will have to sort out with some if/else statements anyway, for example:
if (code.equals(code1))
someString = getcode1(row);
else if (code.equals(code2))
somestring = getcode2(row);
else if (code.equals(code3))
someString = getcode3(row);
else
someString = getcode4(row);
Why not use this:
if (code.equals(code1))
someString = row.getString("content1");
else if (code.equals(code2))
somestring = row.getString("content2");
else if (code.equals(code3))
someString = row.getString("content3");
else
someString = row.getString("content4");
The first one I can see being used, but there is alternative there. Give us entire minimal requirement code with entire class and methods and we could help you far more than using these snippets of code.
Create an enum for mapping of code and constant. In that enum, create a generic method where you can get code just passing through parameter.
public enum MyEnum {
CODE1("constant1"), CODE2("constant2");
private String constant;
public String getConstant() {
return constant;
}
private MyEnum(String constant) {
this.constant = constant;
}
private static String getConstant(String code) {
return Arrays.stream(MyEnum.values()).filter(mapping -> mapping.name().equalsIgnoreCase(code))
.map(e -> e.getConstant()).findAny().orElse(null);
}
public static String getCode(Row row, String code) {
String constant = getConstant(code);
return constant != null ? row.get(constant) : null;
}
}
You can get code from row object by calling genric method getCode().
MyEnum.getCode(row, "code1")

Add null-like value to String in Java

I am writing test method like setTask(Task task). And Task object has several fields, e.g.
public String vehicle;
Method setTask should be used in different test-cases, so I'd like to have an options for this field to accept values:
null - the method should not do anything in this particulare case;
some string value - e.g. "", "Hello, World!", "Iso Isetta", ...
random - a value that indicates (as well as null indicates "no changes") that a random value should be selected for a drop-down list corresponding to this field.
So what can I do to make String to be SpecialString which could accept values null, random & some string value? (BTW: I don't want to set it to string value "RANDOM", and chech whether the value is equal to "RANDOM"-string)
UPDATE: I don't mean random like random value from a set of values, I mean random as well as null and this is for setTask() to handle random (select random from drop-down), and not to pass a random string from a set of values.
Pseudocode:
Task task = new Task();
task.vehicle = random; // as well as null
setTask(task)
in setTask(Task task):
if (task.vehicle == null) {
//skip
} else if (task.vehicle == random) {
// get possible values from drop-down list
// select one of them
} else {
// select value from drop-down list which is equal to task.vehicle
}
Don't assign a fixed String but use a Supplier<String> which can generate a String dynamically:
public Supplier<String> vehicleSupplier;
This, you can assign a generator function as you request:
static Supplier<String> nullSupplier () { return () -> null; }
static Supplier<String> fixedValueSupplier (String value) { return () -> value; }
static Supplier<String> randomSupplier (String... values) {
int index = ThreadLocalRandom.current().nextInt(values.length) -1;
return index > 0 && index < values.length ? values[index] : null;
}
In use, this looks like:
task.setVehicleSupplier(nullSupplier()); // or
task.setVehicleSupplier(fixedValueSupplier("value")); // or
task.setVehicleSupplier(randomSupplier("", "Hello, World!", "Iso Isetta"));
and you can get the String by
String value = task.vehicleSupplier().get();
or hide the implementation in a getter function
class Task {
// ...
private Supplier<String> vehicleSupplier;
public void setVehicleSupplier(Supplier<String> s) {
vehicleSupplier = s;
}
public String getVehicle() {
return vehicleSupplier != null ? vehicleSupplier.get() : null;
}
// ...
}
What you may want to do is to create an object that wraps a string as well as some information about whether or not it's a special value. Something along the lines of...
public class Special<T> {
public enum Type {
NOTHING, RANDOM, SPECIFIC
}
private final Type type;
private final T specificValue;
public Special(Type type, T specificValue) {
this.type = type;
this.specificValue = specificValue;
}
public Type getType() {
return type;
}
public T getSpecificValue() {
if (type != SPECIFIC) {
throw new IllegalStateException("Value is not specific");
}
return specificValue;
}
}
The class above could be used like so:
Special<String> a = new Special<>(Special.Type.NOTHING, null);
Special<String> b = new Special<>(Special.Type.SPECIFIC, "Hello");
if (b.getType() == Special.Type.RANDOM) {
// do something
}else if (b.getType() == Special.Type.SPECIFIC) {
String val = b.getSpecificValue();
// do something else
}
A slightly more polished variant of the thing above is probably the best way, but there is a way, a much uglier way, to do it using nothing but a String field.
What you could do is to have a "magical" string instance that behaves differently from all other string instances, despite having the same value. This would be done by having something like
static final String SPECIAL_VALUE_RANDOM = new String("random");
Note the use of the String constructor, which ensures that the string becomes a unique, non-interned instance. You can then say if (vehicle == SPECIAL_VALUE_RANDOM) { ... } (note the use of == instead of .equals()) to check if that specific instance (rather than any other string that says "random") was used.
Again, this is not a particularly good way of doing this, especially if you intend to do this more than once ever. I would strongly suggest something closer to the first way.

how can I improve my code for this task

I am newbie to object orientated programming and trying to construct something which resembles a basic vote counter which should take an int parameter that represents a choice of two candidates and print the election results to the terminal window. albeit (the votes attributable to each candidate and the total votes cast)
The method I am looking for should also return a string that gives information on the success or failure of casting the vote.”your vote has been cast” “invalid choice, no vote cast"
I have created a class and the constructors and also implemented some basic get methods.
I am wondering how I should go about achieving this objective albeit through a conditional statement or using some sort of advanced method.
any help in terms of the syntax or wider approach would be appreciated.
public class VoteCounter {
private String candidate1;
private String candidate2;
private int candidate1Votes;
private int candidate2Votes;
private boolean completed;
public VoteCounter(String candidate1, String candidate2) {
this.candidate1 = candidate1;
this.candidate2 = candidate2;
this.candidate1Votes = 0;
this.candidate2Votes = 0;
this.completed = false;
}
public VoteCounter() {
this("CANDIDATE 1", "CANDIDATE 2");
}
public String getCandidate1 () {
return this.candidate1;
}
public String getCandidate2 () {
return this.candidate2;
}
public Boolean getCompleted () {
return this.completed;
}
public void setCompleted (boolean completed) {
this.completed = completed;
}
}
Something like this?
private String vote(int choice)
{
if(choice == 1)
{
candidate1Votes++;
}
else if(choice == 2)
{
candidate2Votes++;
}
else
{
return "invalid choice, no vote cast";
}
return "your vote has been cast";
}
I would do that in more general manner, avoiding code duplication and allowing to change number of candidates easily.
So let's make a class Vote similar to your VoteCounter but only for one candidate, with following fields:
private String candidate; // init this in constructor
private int candidateVotes; // initially 0, so no need to init
and with vote() method like in other answer but also without a candiadate, so:
public void vote() {
candidateVotes++;
}
Then you can make class VoteCounter which will take any number of candidates and will keep them in Array or Map.
Map<Integer, Vote> votes = new HashMap<>();
then you're creating vote method with choice:
public void vote(int choice) {
votes.get(choice).vote();
}
Then all is left is to iterate through your votes map and find the one with biggest number of votes.

Minimise if else conditions in method

I have the following class.
public enum EnumService
{
ONE, TWO, THREE, FOUR,.............HUNDRED;
//Values till HUNDRED
public static EnumService returnMockService(String request)
{
//some string match
if(request.matches("/abc*")){
return ONE;
}
//some other string is match
else if(request.matches("/bcd*"))
return TWO;
else if(request.matches("/decf*"))
return THREE;
//many else if conditions
else if(request.matches("/wxyz*"))
return HUNDRED;
return null;
}
}
The code is not standard with more if else statements.
I want to minimize the number of if calls in above method yet maintaining the return type as EnumService
Any better option to do this.
It would be great if some can help me in making it clean.
First: no need to else if you return.
Second: you can optimize it a LOT if you use this string as a parameter in the enum:
public enum EnumService
{
ONE("abc*"),
// etc
private static final Map<Pattern, EnumService> MAPPING
= new HashMap<>();
static {
for (final EnumService svc: values())
MAPPING.put(svc.pattern, svc);
}
private final Pattern pattern;
EnumService(final String s)
{
pattern = Pattern.compile(s);
}
public static EnumService returnMockService(String request)
{
for (final Map.Entry<Pattern, EnumService> entry: MAPPING.entrySet())
if (entry.getKey().matcher(request).matches())
return entry.getValue();
return null;
}
}
I would put the strings that you're matching along with the EnumService value they should map to into an array of simple objects, then loop through the array.
E.g.:
ArrayEntry[] entries = new ArrayEntry[] {
new ArrayEntry("/abc*", EnumService.ONE),
// ...and so on...
};
and then:
for (ArrayEntry entry : entries) {
if (request.matches(entry.str)) {
return entry.value;
}
}
return null;
...where ArrayEntry is just a simple class with those two properties (str and value).
If you don't want to have ArrayEntry, you can use a Map:
Map<String,EnumService> entries = new HashMap<String,EnumService>();
entries.put("/abc*", EnumService.ONE);
//...and so on...
and then
for (Map.Entry<String,EnumService> entry : entries.entrySet()) {
if (request.matches(entry.getKey())) {
return entry.getValue();
}
}
return null;
Or you can do it with parallel arrays instead:
String[] strings = new String[] { "/abc*", /*...and so on...*/ };
EnumService[] values = new EnumService[] { EnumService.ONE, /*...and so on...*/ };
and then
int n;
for (n = 0; n < strings.length; ++n) {
if (request.matches(strings[n])) {
return values[n];
}
}
return null;
But parallel arrays tend to be a bit of a maintenance issue.
You can go for design patterns, best for these kind of things are state pattern. State Pattern is to solve this kind of issues and make the code more compatible & flexible. Look over this http://www.javacodegeeks.com/2013/08/state-design-pattern-in-java-example-tutorial.html
Have a look at the below link.
Why can't I switch on a String?
As stated there, if you are using jdk 7, just use switch on the strings. If not, create an enum of /abc*,/bcd*,etc. and use them in switch.
Hope this helps.
Or you could always, store them in an array and loop through them. That would be much easier but costs an extra array.
If there is no logic to map /abc to ONE and /bcd to TWO etc, then maybe you need to load these into an array or map, then simply get the index of the array where it matches.
myMap.put("abc", ONE);
myMap.put("bcd", TWO);

Sorting Strings in arbitrary order

I have a group of Strings which represent product sizes in which most of them are duplicated in meaning but not name. (IE the size Large has at least 14 different spellings possible, each of which needs to be preserved.) I need to sort these based on the size they represent. Any possible Small value should come before any possible Medium value etc.
The only way I see this being possible is to implement a specific Comparator which contains different Sets grouping each size on the base size it represents. Then I can implement the -1,0,1 relationship by determining which Set that particular size falls into.
Is there a more robust way to accomplish this? Specifically I'm worried about 2 weeks from now when someone comes up with yet another way to spell Large.
edit: to be clear its not the actual comparator I have a question with, its the setup with the sets containing each group. Is this a normal way to handle this situation? How do I future proof it so each new size addition doesn't require a full recompile / deploy?
Custom comparator is the solution. I do not understand why do you worry that this is not robust enough.
A simple approach would be to load the size aliases from a resourcebundle. Some example code (put all the files in the same package):
An interface to encapsulate the size property
public interface Sized {
public String getSize();
}
A product class
public class Product implements Sized {
private final String size;
public Product(String size) {
this.size = size;
}
public String getSize() {
return size;
}
#Override
public String toString() {
return size;
}
}
A comparator that does the magic:
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
public class SizedComparator implements Comparator<Sized> {
// maps size aliases to canonical sizes
private static final Map<String, String> sizes = new HashMap<String, String>();
static {
// create the lookup map from a resourcebundle
ResourceBundle sizesBundle = ResourceBundle
.getBundle(SizedComparator.class.getName());
for (String canonicalSize : sizesBundle.keySet()) {
String[] aliases = sizesBundle.getString(canonicalSize).split(",");
for (String alias : aliases) {
sizes.put(alias, canonicalSize);
}
}
}
#Override
public int compare(Sized s1, Sized s2) {
int result;
String c1 = getCanonicalSize(s1);
String c2 = getCanonicalSize(s2);
if (c1 == null && c2 == null) {
result = 0;
} else if (c1 == null) {
result = -1;
} else if (c2 == null) {
result = 1;
} else {
result = c1.compareTo(c2);
}
return result;
}
private String getCanonicalSize(Sized s1) {
String result = null;
if (s1 != null && s1.getSize() != null) {
result = sizes.get(s1.getSize());
}
return result;
}
}
SizedComparator.properties:
1 = Small,tiny
2 = medium,Average
3 = Large,big,HUGE
A unit test (just for the happy flow):
import org.junit.Before;
import org.junit.Test;
public class FieldSortTest {
private static final String SMALL = "tiny";
private static final String LARGE = "Large";
private static final String MEDIUM = "medium";
private Comparator<Sized> instance;
#Before
public void setup() {
instance = new SizedComparator();
}
#Test
public void testHappy() {
List<Product> products = new ArrayList<Product>();
products.add(new Product(MEDIUM));
products.add(new Product(LARGE));
products.add(new Product(SMALL));
Collections.sort(products, instance);
Assert.assertSame(SMALL, products.get(0).getSize());
Assert.assertSame(MEDIUM, products.get(1).getSize());
Assert.assertSame(LARGE, products.get(2).getSize());
}
}
Note that ResourceBundles are cached automatically. You can reload the ResourceBundle programmatically with:
ResourceBundle.clearCache();
(since Java 1.6). Alternatively you could use some Spring magic to create an auto-reloading message resource.
If reading from a rickety properties file is not cool enough you could quite easily keep your size aliases in a database too.
To impose an arbitrary ordering on a collection of strings (or objects in general), the standard means to do this is to implement a Comparator as you suggest.
Apart from the 'manual' solution you suggest, you could consider comparing the relative edit distance of strings to canonical examples. This will be more flexible in the sense that it will work on alternatives you haven't thought of. But in terms of the work involved, it might be overkill for your application.

Categories

Resources