patternjavaModerate
Selecting an enum for a payment transaction
Viewed 0 times
paymentenumtransactionforselecting
Problem
I'm writing a small Android application that deals with payments and, in my function that handles transactions, I need to translate a number of installments (1-12) and whether or not it has interest (0-1) into the de facto objects for those of the SDK I'm using.
Right now I have a switch that looks like the following:
Now this is without me having implemented interest which in this method would require another massive switch statement where
My thinking is that there must be a smarter way of doing this that
Right now I have a switch that looks like the following:
InstalmentTransactionEnum install = InstalmentTransactionEnum.ONE_INSTALMENT;
//TODO: Implement interest
switch (installments) {
case "1":
install = InstalmentTransactionEnum.ONE_INSTALMENT;
break;
case "2":
install = InstalmentTransactionEnum.TWO_INSTALMENT_NO_INTEREST;
break;
case "3":
install = InstalmentTransactionEnum.THREE_INSTALMENT_NO_INTEREST;
break;
case "4":
install = InstalmentTransactionEnum.FOUR_INSTALMENT_NO_INTEREST;
break;
case "5":
install = InstalmentTransactionEnum.FIVE_INSTALMENT_NO_INTEREST;
break;
case "6":
install = InstalmentTransactionEnum.SIX_INSTALMENT_NO_INTEREST;
break;
case "7":
install = InstalmentTransactionEnum.SEVEN_INSTALMENT_NO_INTEREST;
break;
case "8":
install = InstalmentTransactionEnum.EIGHT_INSTALMENT_NO_INTEREST;
break;
case "9":
install = InstalmentTransactionEnum.NINE_INSTALMENT_NO_INTEREST;
break;
case "10":
install = InstalmentTransactionEnum.TEN_INSTALMENT_NO_INTEREST;
break;
case "11":
install = InstalmentTransactionEnum.ELEVEN_INSTALMENT_NO_INTEREST;
break;
case "12":
install = InstalmentTransactionEnum.TWELVE_INSTALMENT_NO_INTEREST;
break;
}
stoneTransaction.setInstalmentTransactionEnum(install);Now this is without me having implemented interest which in this method would require another massive switch statement where
install would be equal to TWO_INSTALLMENT_WITH_INTEREST and so on.My thinking is that there must be a smarter way of doing this that
Solution
You're using the wrong tool for the job.
If there's ever going to be a loan that I will pay an amount to every week for 2 years, you're gonna have to write a script to generate the code for you, because doing it by hand would suck. You'd have to write code for 204 different enum values, including the switch cases.
If you had to map months to strings, you could have given your enums values, e.g.
Like that.
But what you have here is more like an object. You have two values to keep track of, and they are not related. This doesn't lend itself to an enumeration as you'll need to create each combination by hand.
For that, I recommend using value objects. An example of a value object is a String. It's an immutable object of which you cannot change the value.
So consider making a class like so:
I didn't implement the full thing, that's something you'll have to do, but this will give you the benefit of object equality combined with the ability to reference instances (
I also opted for
The upsides of this approach are that it automatically supports all values of installments and interest.
The downside is that if you have a lot of values, you risk filling up your memory with all of them. You don't have this, though, as right now it's going to be 24 values (12 months * 2). For Android development, it might not even matter - apps don't have the same active duration as server applications do.
If there's ever going to be a loan that I will pay an amount to every week for 2 years, you're gonna have to write a script to generate the code for you, because doing it by hand would suck. You'd have to write code for 204 different enum values, including the switch cases.
If you had to map months to strings, you could have given your enums values, e.g.
enum Month {
JANUARY(1, "January", "JAN"),
FEBRUARI(2, "February", "FEB"),
...
private int number;
private String name;
private String shorthandName;
Month(int month, String fullName, String shortName){
number = month;
name = fullName;
shorthandName = shortName;
}
//add getters....
}Like that.
But what you have here is more like an object. You have two values to keep track of, and they are not related. This doesn't lend itself to an enumeration as you'll need to create each combination by hand.
For that, I recommend using value objects. An example of a value object is a String. It's an immutable object of which you cannot change the value.
So consider making a class like so:
class InstallmentTransaction {
private final int installments;
private final boolean interest;
private static final Map> instances = new HashMap<>();
private static final Object lockObject = new Object();
private InstallmentTransaction(int installments, boolean interest)
{
this.installments = installments;
this.interest = interest;
}
public static InstallmentTransaction getInstance(int installments, boolean interest){
synchronized(lockObject){
Map map = instances.get(installments);
if(map == null){
map = new HashMap<>();
instances.put(installments, map);
}
InstallmentTransaction instance = map.get(interest);
if(instance == null){
instance = new InstallmentTransaction(installments, interest);
map.put(interest, instance);
}
return instance;
}
}
//getters go here
}I didn't implement the full thing, that's something you'll have to do, but this will give you the benefit of object equality combined with the ability to reference instances (
getInstance(12, false)).I also opted for
Integer here, because that's the format you store numbers in. Even if the API only exposes Strings, that doesn't mean you are stuck using Strings. ""+int will give you a String, and Integer.parseInt("") will give you an integer.synchronized is used to prevent there being two threads both calling getInstance at the same time and creating two objects for the same values.The upsides of this approach are that it automatically supports all values of installments and interest.
The downside is that if you have a lot of values, you risk filling up your memory with all of them. You don't have this, though, as right now it's going to be 24 values (12 months * 2). For Android development, it might not even matter - apps don't have the same active duration as server applications do.
Code Snippets
enum Month {
JANUARY(1, "January", "JAN"),
FEBRUARI(2, "February", "FEB"),
...
private int number;
private String name;
private String shorthandName;
Month(int month, String fullName, String shortName){
number = month;
name = fullName;
shorthandName = shortName;
}
//add getters....
}class InstallmentTransaction {
private final int installments;
private final boolean interest;
private static final Map<Integer, Map<Boolean, InstallmentTransaction>> instances = new HashMap<>();
private static final Object lockObject = new Object();
private InstallmentTransaction(int installments, boolean interest)
{
this.installments = installments;
this.interest = interest;
}
public static InstallmentTransaction getInstance(int installments, boolean interest){
synchronized(lockObject){
Map<Boolean, InstallmentTransaction> map = instances.get(installments);
if(map == null){
map = new HashMap<>();
instances.put(installments, map);
}
InstallmentTransaction instance = map.get(interest);
if(instance == null){
instance = new InstallmentTransaction(installments, interest);
map.put(interest, instance);
}
return instance;
}
}
//getters go here
}Context
StackExchange Code Review Q#122824, answer score: 16
Revisions (0)
No revisions yet.