How to drive appropriate controller at runtime based on parameters

Viewed 43

How to Write a interface based RestController to call appropriate implemented controller based on the request params,

  1. /start?param=bmw should invoke BMW Controller
  2. /start?param=volvo should invoke Volvo Controller

Interface called "Car" with single abstract "startEngine" :


@RestController("/start")
interface Car {
  String startEngine();
}

class BMW implements Car{
    @Override
    @GetMapping("/{param}")  // bmw
    public String startEngine() {

     return "BMW Started";
  
    }
}


class Volvo implements Car{
    @Override
    @GetMapping("/{param}")  // volvo
    public String startEngine() {

     return "Volvo Started";
  
    }
}

How Can we achieve above use case.

Expose a GET REST Endpoint as "/start" which would accept a queryparam "param" accepting String values "bmw" or "Volvo”.

1 Answers

I don't see any benefits in having a common interface for 2 controllers -- a controller bean is an "entry point" for requests, and its methods are not invoked by the application code (they are called by the MVC framework instead), so why to hide the controllers behind the interface?

Also in the provided example having 2 controllers looks like a code duplication -- they both do essentially the same thing, with the only difference in a configuration. Just imagine if you add new cars (toyota, tesla, etc.) -- you definitely don't want to introduce new controller each time.


How Can we achieve above use case, using switch case or if condition based to drive appropriate bean at runtime

Usually using if-statements and switch-cases for such situations is considered as a code smell and would lead quickly to unmaintainable code, as far as number of new cases (introducing new cars in the example) grow.

I like to use the Strategy pattern in such situations, and here is an example how it might be implemented using the Map:

@RestController("/start")
class CarController {

    // strategies map, holds <'bmw', 'BMW Started'> and <'volvo', 'Volvo Started'>
    private final Map<String, String> engines;

    @GetMapping("/{param}")
    public String startEngine(String param) {
        return engines.getOrDefault(param, null);
    }
}
Related