So the similar type of questions are answered elsewhere but here I am expecting the best way to omit if else chain for the given situation.
CURRENT CODE
private ViewModel getViewModel(Receipt receipt) {
String receiptType = receipt.type;
if(receiptType.equals("HOTEL")) {
return new HotelReceiptViewModel(receipt));
} else if(receiptType.equals("CAR")) {
return new CarReceiptViewModel(receipt));
}
.
.
.
} else if(receiptType.equals("LUNCH")) {
return new FoodReceiptViewModel(receipt));
}
}
where all the view models extend a class called ReceiptViewModel. e.g.
public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
public HotelReceiptViewModel(Receipt receipt) {
super(receipt);
this.receiptNumber = receipt.getDocumentNumber();
this.receiptHeading = "HOTEL";
}
}
There are currently 5 types of receipts and there will be 3-4 more types of receipts in future.
POSSIBLE SOLUTIONS
- Use of
HashMap - Use of
Enum - Use Strategy Pattern or Command Pattern
- Use of Reflection
Let's see pros and cons of each approach
1. Use of HashMap
private ReceiptViewModel getViewModel(Receipt receipt) {
Map<String, ReceiptViewModel> map = getViewModelsMap();
String receiptType = receipt.type;
ReceiptViewModel viewModel = map.get(receiptType);
if(viewModel != null) {
viewModel.setReceipt(receipt);
}
return viewModel;
}
private Map<String, ReceiptViewModel> getViewModelsMap() {
Map<String, ReceiptViewModel> map = new HashMap<String, ReceiptViewModel>();
map.add("HOTEL"), new HotelReceiptViewModel());
map.add("CAR"), new CarReceiptViewModel());
map.add("LUNCH"), new FoodReceiptViewModel());
}
and ReceiptViewModel classes will look like
public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
public HotelReceiptViewModel(Receipt receipt) {
super(receipt);
this.receiptNumber = receipt.getDocumentNumber();
this.receiptHeading = "HOTEL";
}
}
PROS Faster, Easier, Extensible.
CONS ReceiptViewModel object does not require an object of Receipt type in the constructor. Receipt is rather set using a setter, where all the logic of initializing the ReceiptViewModel class will move now.
2. Use of Enum
private ReceiptViewModel getViewModel(Receipt receipt) {
String receiptType = receipt.type;
ReceiptViewModel viewModel =
ReceiptViewModels.valueOf(receiptType).getReceiptViewModel(receipt);
return viewModel;
}
And Enum will look like
public enum ReceiptViewModels {
HOTEL(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new HotelReceiptViewModel(receipt);
}
),
CAR(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new CarReceiptViewModel(receipt);
}
),
.
.
.
LUNCH(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new FoodReceiptViewModel(receipt);
}
),
public abstract ReceiptViewModel getReceiptViewModel(Receipt receipt);
}
PROS Fast, Probably Easy.
CONS Size of Enum will keep on increasing as the receipt types increase, resulting in non-maintainable code.
ReceiptViewModels.valueOf(receiptType) expects a known receipt type. If a new receipt type comes as a response from server, it will result in an IllegalArgumentException
3. Use of Reflection
Class<? extends ReceiptViewModel> viewModel = Class.
forName(receiptType + name + "ReceiptViewModel").asSubclass(ReceiptViewModel.class);
ReceiptViewModel receiptViewModel = viewModel .newInstance();
CONS 1. Slower
-
Cannot be used when the class names are different. e.g. For LUNCH type the view model class name is FoodReceiptViewModel
-
Logic of getting values from receipt is moved to a setter instead of constructor as in case of HashMap
4. Use of Strategy Pattern or Template Pattern
PROS Easy to understand and faster than Reflection
CONS Probably an overkill. A new class will be added for each type of receipt.
Considering all the above points, which would be the best approach for my use case to remove the multiple if-else blocks?
Aucun commentaire:
Enregistrer un commentaire