4

I have the following scenario:

public abstract class BaseTask{...}

public class TaskA extends BaseTask {....}

public class TaskB extends BaseTask {....}

public interface TaskService<T extends BaseTask>{
        void process(T task);
}

@Service @Qualifier("taskServiceA")
public class TaskServiceA<TaskA> implements TaskService<TaskA>{
}

@Service @Qualifier("taskServiceB")
public class TaskServiceB<TaskB> implements TaskService<TaskB>{
}

public class ProcessingService{

        @Autowired @Qualifier("taskServiceA")
        private TaskService<TaskA> taskAService;

        @Autowired @Qualifier("taskServiceB")
        private TaskService<TaskB> taskBService;

        public void process(Order o){
            BaseTask task = o.getTask();
            getTaskService(o).start(task);
        }

        private <T extends BaseTask> TaskService<T> getTaskService(Order o){
            if("atype".equals(o.type)){
              return (TaskService<T>) taskAService;
            } else if("btype".equals(o.type)){
              return (TaskService<T>) taskBService;
            }
        }
}

Update: I have reworded the question because the answers I was getting was not what I was looking for.

My questions is related to the getTaskService method.

  1. Why do I need to cast the return value like this

    return (TaskService) taskAService;

  2. Is there another way to implement the getTaskService() method without having to do the cast?

I will really appreciate if someone can provide some explanation or better implementation for the getTaskService method.

4
  • this code doesn't seem to compile Commented Nov 13, 2018 at 9:14
  • 1
    Don't use == with Strings: use a switch statement or .equals() Commented Nov 16, 2018 at 6:03
  • @AndrewTobilko I just wrote pseudo code to be able to ask my question. Commented Nov 16, 2018 at 6:12
  • @MauricePerry I update my code basing on your comment. Commented Nov 16, 2018 at 6:12

2 Answers 2

1

How about this?

  • No need of any if conditions.
  • Later if someone does add another implementation of BaseTask they don't have to change any other code.
  • Also I recommend changing "atype" to Enum and using Map<EnumTask, ? extends BaseTask> serviceMap; instead of String.

Your final invocation of Tasks can be without any checks

@Service
class ProcessingService { 

        @Autowired 
        private TaskServiceManager taskServiceManager;

        public void process(Order o){
            taskServiceManager.getServiceTask(o.type).start(task);
        }
}

Other classes

enum ServiceEnum {
    TaskA,
    TaskB
}

public class TaskA extends BaseTask {....}
public class TaskB extends BaseTask {....}

public abstract class TaskService<T extends BaseTask>{

    public TaskService(ServiceEnum serviceEnum, TaskServiceManager taskServiceManager) {
        taskServiceManager.registerTask(serviceEnum, this);
    }
    void process(T task);
}

@Service @Qualifier("taskServiceA")
public class TaskServiceA<TaskA> implements TaskService<TaskA>{
        @Autowired 
        public TaskA(TaskServiceManager taskServiceManager) {
              super(ServiceEnum.TaskA, taskServiceManager);
        }
}

@Service @Qualifier("taskServiceB")
public class TaskServiceB<TaskB> implements TaskService<TaskB>{...}

@Service
class  TaskServiceManager {
    Map<ServiceEnum, ? extends TaskService> serviceMap;

    public <T extends TaskService> void registerTask(ServiceEnum serviceName, T task) {
        if(serviceMap.containsKey(serviceName)) {
            throw new IllegalArgumentException("ServiceName is already in the Map");
        }
        serviceMap.put(serviceName, task);
    }

    public <T extends TaskService> T getServiceTask(ServiceEnum serviceName) {
        if(!serviceMap.containsKey(serviceName)) {
            throw new IllegalArgumentException("ServiceName is not Registered");
        }
        return serviceMap.get(serviceName);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

@AndrewTobilko Edited it, the logic can be only inside the Base Class. That way the child Tasks will never have to know about the manager. But Yes, BaseTask will still know about it. That should still be fine as Manager class here is just a friend or helper type of class to keep track of the different tasks which are running.
Children still would require a TaskServiceManager
No change, the same way a Service class was instantiated. Since its spring, you'll only have to annotate it with @Service (Added it to the answer). Basically a SIngleton class and inject the Manager class into BaseTask. If using Spring, we could directly Inject the Manager into Base class without having to send it from child. But wouldn't work if there is no DI framework. Any suggestions on how to make it better?
Tasks aren't beans, they are extracted from an order
Ah, I understood it wrongly. I was trying to solve for TaskService and identifying them using conditions. Modified it to accomodate TaskService. I'll see if I can better the answer.
|
1

Because type T is resolved wherever the method is used. The following statement is valid:

    TaskService<TaskA> s = getTaskService(o);

So is:

    TaskService<TaskB> s = getTaskService(o);

So within the method getTaskService, you don't know much about T.

The correct way to do this would be:

private TaskService<? extends BaseTask> getTaskService(Order o) {
    if ("atype".equals(o.type)) {
        return taskAService;
    } else if ("btype".equals(o.type)) {
        return taskBService;
    } else {
        return null;
    }
}

The assignment above would have to become:

TaskService<? extends BaseTask> s = getTaskService(o);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.