Polymorphism - Spring java services and controllers

In my project i have some class hierarchy for tasks:

         BaseTask

      /            \ 
CriticalTask         MarginalTask

I have the following service:

@Transactional
@Service
public class TaskService implements ITaskService  {

   public void execute(CriticalTask  task) {
      ...
   }

   public void execute(MarginalTask task) {
      ...
   }
}

I would like to have the following controller:

@RequestMapping(value = "/executeTask", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(value = HttpStatus.OK)
public void executeTask(@RequestBody BaseTaskDTO baseTaskDTO , HttpServletRequest request) {

    this.taskService.execute(baseTaskDTO);

}

and i would like this service to actually call the "execute" method with the corresponding task.

too bad this is not working.

I know polymorphism can help me on this goal, i.e. all the tasksDTOs can implement the "execute" method and then i simply call the baseTask.execute(). The problem with this is that it feels really awkward to put some logic inside the DTO (were only data should be exists and nothing else)

Are there any good practices to solve my issue?

Answers


Your code won't even compile indeed as the dynamic polymorphism is for method calls, not for parameters. Here you are calling an overloaded method that does not exist. A simple "hack" could be:

@Transactional
@Service
public class TaskService implements ITaskService  {

    public void execute(BaseTask task){
      if( task instanceof CriticalTask){
         execute((CriticalTask) task);
      else
         execute((MarginalTask) task);
    }

    void execute(CriticalTask  task) {
      ...
    }

    void execute(MarginalTask task) {
      ...
    }
}

I feel like you are doing things backwards. The Task should know what it does and how it does it. It should have a run or execute method (like Runnable). Your TaskService then only needs to invoke that method on the given argument

public void execute(BaseTask task) { // <-- doesn't matter what implementation it is
    task.execute();
}

That's where polymorphism comes into play.


You could think about a TaskExecutor, that will be responsible to execute your task and a Map<Class<?>,TaskExecutor> that will be a field in TaskService and will be used to fetch the right TaskExecutor to execute a given task.

In your service you could have (among other methods) something like:

@Override
public void execute(TaskDTO task) {
    taskExecutors.get(task.getClass()).execute(task);
}

this would imply that you will need all DTO classes you want to execute declared in that map with a given TaskExecutor associated.

You controller will just parse it to the right DTO object, and give to the TaskService, that will define the best TaskExecutor to perform the execution. execute method will be invoked and will invoke with that DTO as parameter. One possible TaskExecutor definition would be something like:

public interface TaskExecutor {

    void execute(TaskDTO taskDTO);

} 

at the end you will have one execute method in TaskService but different implementation of TaskExecutor, that will handle specific TaskDTO implementations. This uses some concepts of Strategy Design Pattern

This will solve your requirement in which the execution method should be decopled from the DTO itself.


Need Your Help

Submitting form and success message on same page?

javascript php jquery html forms

So right now I have a basic contact.html with a php script set to email me if someone on my website contacts me. When they click submit, it takes them to contact_sent.php, which is a blank page. I ...

Why does len() not support iterators?

python iterable

Many of Python's built-in functions (any(), all(), sum() to name some) take iterables but why does len() not?