0

I need to create a mapper object based on the input type, is there a simple solution without switch case or if else conditions? Below is an example. Can you please help me to provide a simple working example for this?

Mapper mapper = null;
if(requestType=="AAA"){
    mapper = RequestMapperAAA.createTypeRequest(inputpayload);
} else if(requestType=="BBB"){
    mapper = RequestMapperBBB.createTypeRequest(inputpayload);
} else if(requestType=="CCC"){
    mapper = RequestMapperCCC.createTypeRequest(inputpayload);
} else if(requestType=="DDD"){
    mapper = RequestMapperDDD.createTypeRequest(inputpayload);
}  so on.. upto 20 input types
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Devi
  • 73
  • 1
  • 7
  • 1
    Take a look here: https://www.baeldung.com/java-factory-pattern or https://www.digitalocean.com/community/tutorials/java-dependency-injection-design-pattern-example-tutorial or https://www.baeldung.com/java-replace-if-statements – Harry Coder Mar 17 '23 at 09:40
  • Is there any other alternative using java reflections to create mapper implementations dynamically by request type? – Devi Mar 17 '23 at 11:40
  • You can do it with reflection. When using Spring or another DI framework you can do it without implementing reflection yourself. – Harry Coder Mar 17 '23 at 12:42

2 Answers2

1

Here's one way.

Have a map of type Map<String, Function<InputPayLoad, Mapper>> where InputPayLoad is the type passed into createTypeRequest methods.

You can pre-populate it for each request type with a Function which calls createTypeRequest on the appropriate RequestMapper and returns a Mapper.

Map<String, Function<InputPayload, Mapper>> requestTypeToMapperCreatorFunction = new HashMap<>();
requestTypeToMapperCreatorFunction.put("AAA", RequestMapperAAA::createTypeRequest);
requestTypeToMapperCreatorFunction.put("BBB", RequestMapperBBB::createTypeRequest);
requestTypeToMapperCreatorFunction.put("CCC", RequestMapperCCC::createTypeRequest);
//...

To build a mapper for a given requestType,

Mapper mapper = requestTypeToMapperCreatorFunction.get(requestType)
        .apply(inputPayload);
Thiyagu
  • 17,362
  • 5
  • 42
  • 79
1

I will give a more detailed answer here, by responding to you question about using reflection. There is a good example here if you want to use reflection.

You don't have to implement reflection to do this. The response of @Thiyadu is good also. Instead if you want to use factory pattern with reflection, I will suggest you to use a Dependency Injection framework such as Spring. The reflection phase is already made for you by Spring.

An example with Spring will be the following:

public interface Mapper {
    
}

RequestMapperAAA.java

@Component("AAA")
public class RequestMapperAAA implements Mapper {

}

RequestMapperBBB.java

@Component("BBB")
public class RequestMapperBBB implements Mapper {

}

RequestMapperCCC.java

@Component("CCC")
public class RequestMapperCCC implements Mapper {

}

Usage:

@Autowired
private Map<String, Mapper> requestMapper;

public void userRequestMapper() {
    Mapper requestAAA = requestMapper.get("AAA");
}

The good thing about Spring is that, it will automatically populate the Map for you.

Harry Coder
  • 2,429
  • 2
  • 28
  • 32
  • thank you, Can i use the same method for the ServiceImpl and call the services from the controller based on the Input Type ? throwing // Error: cant' make a static reference to the non static method for @Resource(name="serviceMapper") Map> serviceMapper = new HashMap<>(); public void setServiceMap() { serviceMapper.put("AAA", AaaService::createTypeRequest); // Error: cant' make a static reference to the non static method serviceMapper.put("BBB", BbbService::createTypeRequest); serviceMapper.put("CCC", CccService::createTypeRequest); } – Devi Mar 18 '23 at 07:53
  • I tried below Service class -- @service("AAA") public class AaaServiceImpl implements AaaService{ Override public Response createTypeRequest(Request req) { } } Controller class: -- Autowired private Map serviceMapper; response = serviceMapper.get("AAA");//input types AAA, BBB, CCC etc. Config class --- Configuration public class AutoConfig { @Resource(name="serviceMapper") Map> serviceMapper = new HashMap<>(); public void setServiceMap() { serviceMapper.put("AAA", AaaService::createTypeRequest); } – Devi Mar 18 '23 at 07:57
  • In this example how do i call the ServiceImpl method without declaring it in a Map from the controller class response = serviceMapper.get("AAA") – Devi Mar 18 '23 at 07:59
  • Spring will map it automaticaly to you, depending on the ServiceImpl you specify with your key value – Harry Coder Mar 18 '23 at 13:43
  • Take a look here: https://bwgjoseph.com/implement-strategy-pattern-with-spring-boot or you can do a search on Google for "Spring factory pattern" – Harry Coder Mar 18 '23 at 13:57
  • Thanks Harry, this is really a perfect solution for my requirement, only thing is i have different names in service implementation, i need to dynamically pass the method name as well. – Devi Mar 18 '23 at 23:04
  • You don't need to dynamically pass the method name as your classes implements the same interface. Depending on the concrete type, it will call the associates method. – Harry Coder Mar 18 '23 at 23:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/252599/discussion-between-devi-and-harry-coder). – Devi Mar 19 '23 at 04:04
  • Harry, another question: If we are passing the input payload dynamically as a header parameters, how Swagger UI works? thanks. – Devi Apr 13 '23 at 18:18
  • @Devi, this link may help you: https://stackoverflow.com/questions/41180615/how-to-send-custom-headers-with-requests-in-swagger-ui – Harry Coder Apr 13 '23 at 18:27