How to avoid multiple Switch case statements - java

I need to refactor a code that uses multiple case statements based on the queryString value it receives. Everytime a new query is added , we had to add a new case statement. Can we try to simplify it and make it more generic so that we dont have to keep on adding new case statements? Below is the code snippet
switch (queryRequestRecevied) {
case query1:
reply = processQuery1();
break;
case query2:
reply = processQuery2();
break;
case query3:
reply = processQuery3();
break;
case query4:
reply = processQuery4();
break;
case query5:
reply = processQuery5();
break;
case query6:
reply = processQuery6();
break;
default:
reply = processInvalidQuery();
break;
}

The name for the operation you're looking for is Replace Conditional with Polymorphism: Instead of having switch or if statements, use a common interface for your cases and invoke that. I'm presuming here that your query returns some value and using Supplier; if not, use another functional interface or create your own.
Map<String, Supplier<QueryResult>> queryProcessors = new HashMap<>();
...
// in a constructor or similar place
queryProcessors.put(query1, this::processQuery1);
queryProcessors.put(query2, this::processQuery2);
// in your handler method
var result = queryProcessors.getOrDefault(query, this::processInvalidQuery).get();
To decouple even further, you can do this:
// declare some Spring beans implementing this interface
interface QueryProcessor {
String getQuery();
QueryResult processQuery();
}
// in your service class
Map<String, QueryProcessor> queryProcessors;
#Autowired //(not necessary for a single constructor)
MyService(Collection<QueryProcessor> queryProcessors) {
this.queryProcessors = queryProcessors.stream()
.collect(toMap(QueryProcessor::getQuery, identity()));
}

You don't give enough code to see what could be factorized / more generic.
But at least instead of testing your input query you could declare 1 endpoint per query in your #Controller then no need to Switch / case testing.

Related

How to create generic method returning different types extending the same parent

I have created method like this:
public BaseMenu openMenu(MenuItem menuItem) {
utils.clickOnElement(menuItem);
BaseMenu baseMenu = null;
switch (menuItem) {
case FIRST:
baseMenu = new FirstMenu();
break;
case SECOND:
baseMenu = new SecondMenu();
break;
case THIRD:
baseMenu = new ThirdMenu();
break;
}
return baseMenu;
}
It returns type BaseMenu. How to convert it, so that it would return specific menu types (which are extending BaseMenu), like FirstMenu, SecondMenu, etc.?
I tried something like that:
public <T extends BaseMenu> T openMenu(MenuItem menuItem) {
utils.clickOnElement(menuItem);
T baseMenu = null;
switch (menuItem) {
case FIRST:
baseMenu = (T) new FirstMenu();
break;
case SECOND:
baseMenu = (T) new SecondMenu();
break;
case THIRD:
baseMenu = (T) new ThirdMenu();
break;
}
return baseMenu;
}
but it also returned BaseMenu.
How should code look like to work as I am expecting? Is it possible to do it this way? Is there any other way to achieve it?
EDIT:
How am I use it now:
FirstMenu firstMenu = new MainPage().getSomeElementContainingMenu().openMenu(FIRST);
firstMenu.doSomethingFromFirstMenu();
How I want to use it:
new MainPage().getSomeElementContainingMenu().openMenu(FIRST).doSomethingFromFirstMenu();
//where result of openMenu(specificMenu) will be new SpecificMenu(), not BaseMenu() like now
It doesn't have to be based on switch, it was my initial idea, which I have shared here, but it doesn't work as I thought, so I would gladly see also
other possible solutions.
It is not possible to convert this function. Returning BaseMenu was a valid solution which described your use case. It should be the caller's responsibility to cast it to the required type if needed.
The main idea of a generic you let the caller determine the type of the generic so your function can focus on the parts you really cares about. So if I call that function and state that I want that generic to be a class I implemented myself, your function will need to use it.
For example, this code sample would be a valid usage of the generic version.
class Foo extends BaseMenu { /* etc. */ }
Foo foo = openMenu(menuItem);
Alternatively, it may make more sense if you also add methods for firstMenu, secondMenu, etc. You should really only be selecting the menu this way if you only need to use functionality all BaseMenus implement.
public <T extends BaseMenu> T openMenu(MenuItem menuItem) {
utils.clickOnElement(menuItem);
T baseMenu = null;
switch (menuItem) {
case FIRST:
baseMenu = new FirstMenu();
break;
case SECOND:
baseMenu = new SecondMenu();
break;
case THIRD:
baseMenu = new ThirdMenu();
break;
}
return (T) baseMenu;
}
when you call this method! you must use like this:
FirstMenu fm = openMenu(menuItem);
you should not use :
BaseMenu bm = openMenu(menuItem);
cause your method return value is T, that T type is what you handle that return value, if you use BaseMenu to take that return value, that T will be BaseMenu!!

Is it bad practice to use StaticMetamodel (JPA) inside ServiceImpl?

I am rewriting a huge part of an existing application. We have Group an SuperGroup.
Actualy, the groupe can only store one stringValue.
To respect the new scope of the project we want the Group to store multiple value like date, number and boolean. The type of the Group depend on a value that is store inside the SuperGroup. (It also store the format of the group value, but this is not important)
One of the purpose of the application is to search by group value. And here is my problem.
In a service I need to implement the search that will call a DAO and determine on witch value I have to made the search.
My question is in the title. Is it bad practice to use StaticMetamodel (JPA) inside ServiceImpl?
To illustrate my question, here is the service method.
public Groupe getGroupeBySuperGroupeAndEntrepriseAndValue(SuperGroupe superGroupe, Entreprise entreprise, String value) {
if (entreprise == null || StringUtils.isBlank(value)) {
return null;
}
GroupeDataTypeEnum groupeDataTypeEnum = GroupeDataTypeEnum.getGroupeDataTypeByTypeValue(superGroupe.getDataType());
Object finalValue;
SingularAttribute singularAttribute;
switch (groupeDataTypeEnum) {
case STRING_DATA_TYPE:
finalValue = value;
singularAttribute = Groupe_.stringValue;
break;
case NUMBER_DATA_TYPE:
finalValue = FormatUtils.formatStringToDouble(superGroupe.getDataFormat(), value);
singularAttribute = Groupe_.numberValue;
break;
case DATE_DATA_TYPE:
finalValue = FormatUtils.formatStringToDate(superGroupe.getDataFormat(), value);
singularAttribute = Groupe_.dateValue;
break;
case BOOLEAN_DATA_TYPE:
finalValue = FormatUtils.formatStringToBoolean(superGroupe.getDataFormat(), value);
singularAttribute = Groupe_.booleanValue;
break;
default :
finalValue = null;
break;
}
return groupeDAO.getGroupeBySuperGroupeAndEntrepriseAndValue(superGroupe, entreprise, finalValue, singularAttribute);
}
And in the DAO the search is made on the singular attribut passed in parameter.
My problem is purely conceptual. Is it OK to use SingularAttribute that is generated by org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor inside the service. If we left Hibernate I could lost that StaticMetamodel, so we will have bugs. But we can regenerate them because StaticMetamodel is Java stanndard.
Is there a better way to do what I want here?
I need some help to clear my mind. Thank you by advance, any return is welcome.

Nested switches or multiple functions, what would be a better design?

I have a large method that looks something like this
List<Hotel> findAvailHotels(Provider provider, Method method, List<String> codes) {
switch (provider) {
case PROVIDER_1:
//TODO Do common things to provider 1
switch (method) {
case HOTEL_CODE:
break;
case DESTINATION_CODE:
break;
case GEO:
break;
}
break;
case PROVIDER_2:
switch (method) {
case HOTEL_CODE:
break;
case DESTINATION_CODE:
break;
case GEO:
break;
}
break;
}
So each time I need to add a provider I'll need to add a case to that provider and then repeat the method switch for this new provider.
I got a suggestion from a fellow that should be split into methods for each method so for example instead of the above, it'll be
List<Hotel> findAvailHotelsByHotelCode(Provider provider, List<String> codes) {
switch (provider) {
case PROVIDER_1:
//TODO Do common things to provider 1
break;
case PROVIDER_2:
break;
}
List<Hotel> findAvailHotelsByDestinationCode(Provider provider, List<String> codes) {
switch (provider) {
case PROVIDER_1:
//TODO Do common things to provider 1
break;
case PROVIDER_2:
break;
}
List<Hotel> findAvailHotelsByGeo(Provider provider, List<String> codes) {
switch (provider) {
case PROVIDER_1:
//TODO Do common things to provider 1
break;
case PROVIDER_2:
break;
}
Personal thoughts: Maybe splitting into multiple methods makes it more cleaner but if I need to do common stuff to PROVIDER_1 (despite the method) then this common thing will need to be repeated/duplicated in each method (as indicated by the //TODOs in the above code) which kinda means more lines of code but that's a bit irrelevant maybe.
I'd like to hear some thoughts about this, which would you consider more readable and more clean? Any better alternatives?
edit: To give more context, I work with hotel providers.. most providers have 3 common methods of search (hotel_code, destination_code, geo).. from outside this method I can do a hotel_code search for all providers (by looping over the Provider enum and calling the method for each provider with hotel_code enum param).. or I can do it to a specific provider.
Your Question is still a little too abstract to suggest a "best" solution, but Timothy is right so far - in either case you can use polimorphism.
I suggest Strategy pattern because you define the broad structure by using an interface and create an a dedicated class for every single algorithm (provider in your case).
This has at least two advantages:
You have an easy to oversee List of algorithms in the form of classes.
You can replace the outer switch by a loop through your strategy objects.
Hmm - since you asked for it - here is some example code (a little large though ...)
import java.util.ArrayList;
import java.util.List;
public class HotelStuff {
private static class Hotel{/* does whatever ...*/}
private enum SearchMethod{
HOTELCODE,
DESTINATIONCODE,
GEOCODE
}
private interface Providable{
List<Hotel> findAvailHotels(SearchMethod method, List<String> codes);
}
private static class Provider1 implements Providable{
#Override
public List<Hotel> findAvailHotels(SearchMethod method, List<String> codes) {
// TODO create the list ...
return null;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Providable> providers = new ArrayList<Providable>();
providers.add(new Provider1());
// providers.add(new Provider2 .. and so on
List<String> codes = Arrays.asList("123","456");
SearchMethod method = SearchMethod.GEOCODE;
List<Hotel> availableHotels = findAvailHotels(providers, method, codes);
}
public static List<Hotel> findAvailHotels(List<Providable> providers, SearchMethod method, List<String> codes) {
List<Hotel> result = new ArrayList<Hotel>();
List<Hotel> partResult;
for(Providable provider: providers) {
partResult = provider.findAvailHotels(method, codes);
result.addAll(partResult);
}
return result;
}
}
Of course you should implement the classes in seperate files - i just put them into one file to shorten it.
Unless your switch statement is in a factory you should better use polymorphism.
You should look into both the visitor pattern and double dispatch.
The Gang of Four defines the Visitor as:
Represent an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
In your case Provider is the object and Method is the operation.
Double dispatch is useful in situations where the choice of computation depends on the runtime types of its arguments. In your case: you want to do something based on the type of Provider and Method.

How to eliminate this particular SWITCH statement via Polymorphism

I have this switch statement which is executed after the user is shown a list of actions to take and clicks one of them. What we switch on is the action ID
switch (actionId) {
case 0:
openEditProductScreen();
break;
case 1:
startDeleteProductOperation();
break;
case 2:
//nothing
break;
case 3:
openAddProductScreen();
break;
}
I have read some articles on replacing switches with polymorphism but they relate to another type of problem - doing the same thing in different ways (the way you pay different types of employees), but what do I do when I want to trigger a completely different set of actions?
Thinking about it, do I really need to eliminate THIS particular kind of switch statement? I mean, it's readable and logical. What would the benefits be if I eliminated it somehow?
EDIT:
Is this what you meant?
private Map<Integer, ProductRelatedAction> productRelatedActions = new HashMap<Integer, ProductRelatedAction>();
private void mapActionsToIds() {
productRelatedActions.put(0, new EditProductAction());
productRelatedActions.put(1, new DeleteProductAction());
// remainder omitted
}
private abstract class ProductRelatedAction{
abstract void execute();
}
private class EditProductAction extends ProductRelatedAction{
#Override
void execute() {
openEditProductScreen();
}
}
private class DeleteProductAction extends ProductRelatedAction{
#Override
void execute() {
startDeleteProductOperation();
}
}
Add an abstract method execute() in the Action class, create 4 subclasses of Action, overriding execute(). Make the first one execute openEditProductScreen(), the second one execute startDeleteProductOperation(), etc.
Then create one instance of these 4 classes and make the user choose one of those 4 instances.
When the user has chosen the action, call selectedAction.execute().
Should you replace this kind of switch by polymorphism? In my opinion: yes. When you'll have to add another action, there is no way you'll be able to forget to implement the execute() method in the new subclass: your code won't compile without it. On the other hand, Forgetting to add a case in your switch statement is extremaly easy to do. And I'm not even mentioning the fall-through problem of switch statements.

Return 'default' in an int method to pass to switch

So I am making a test class for an assignment for school, and it looks like this:
public class Tester{
//variables.... some for the object to be tested, others for a Base object to be tested against
public void test(SpecialObject objectToBeTested){
SpecialObject baseObject = new SpecialObject();
int testCase = this.compareObjects(objectToBeTested, baseObject); //compare the two
switch (testCase){
case 1: System.out.println("CASE 1"); break;
case 2: System.out.println("CASE 2"); break;
default: System.out.println("Default, everything is good"); break;
//can't just make this case 0, because I think the switch requires a default
//so how do I pass an int to trigger the default?
}
}
public int compareObject(SpecialObject one, SpecialObject two) {
//compare one.this1 and two.that1
if ( one.this1 = two.that1 ) {return 1;};
//compare one.this2 and two.that2
if ( one.this2 = two.that2 ) {return 2;};
//......
// HERE IS WHERE I AM CONFUSED
// I want to do something like
return default;
//or
return "default";
// but can't because I need to pass an int
}
}
Since I am trying to keep all the comparisons in one method (although this will later be abstracted into multiple methods) I am trying to pass an integer back to test() with an integer for the switch method. Trying to keep with good information, I want to tell myself what particular comparison is failing, I think I need to pass something to the switch method to use default. What should I return from the compareObject(one, two) to trigger the default case in the switch method IF everything checks out.
The 'default' in a switch statement is called if the variable doesn't match any of the cases. If you have case 1 and case 2, any int that is NOT 1 or 2 will go to the default block.
You can return anything int other than a 1 or a 2. You choose what you want 'default' to be. Maybe a 0 or a -1 or something else.
Also see Thomas's answer about the switch. Make sure you understand what a `switch' statement does.

Categories

Resources