Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java what design pattern should I use for object instantiation if I have different constructor but same interface?

I'm learning about the design patterns and I encountered a problem which I cant resolve. I'm writing a client/server script. The administrator client send a task with its task data in json format, and the server should instantiate an object accordingly to the recieved task type, and fill its constructor with correct classes. As you can see bellow there are two example class.

public class StartProcessing implements ITask{
    private final IProcessor dataProcessor;

    public StartProcessing(IProcessor dataProcessor){
        this.dataProcessor = dataProcessor;
    }

    @Override
    public void ProcessTask() {
        this.dataProcessor.StartProcess();
    }

}

public class StartQueueFiller implements ITask{

    private IQueueFiller queueFiller;

    public StartQueueFiller(IQueueFiller queueFiller){
        this.queueFiller = queueFiller;
    }

    @Override
    public void ProcessTask() {
        this.queueFiller.Start();
    }

}

interface ITask {
    public void ProcessTask();
}

I've tried something like this, but I'll have like 50 different process and hundreds of tasks, so the constructor will be unmanageable, and I think the factory pattern is not so good in this case, and probably I just miss the point of the pattern. So how you would solve this problem? What should I use instead of the factory pattern?

public class TaskFactory(){

    private final IProcessor processor;
    private final IQueueFiller queuefiller;

    public TaskFactory(IProcessor processor, IQueueFiller queuefiller){
        this.processor = processor;
        this.queuefiller = queuefiller;
    }

    public ITask Create(String task){
        switch(task){
            case "startprocessor":
                return new StartProcessing(this.processor);
            case "startqueuefiller":
                return new StartQueueFiller(this.queuefiller);
        }
    }

}
like image 979
Mayorath Avatar asked Jan 15 '18 16:01

Mayorath


1 Answers

I would just use the Abstract Factory pattern:

public interface TaskFactory {
    Task create();
}

Then we can store a bunch of factories in a data structure of some kind, e.g.:

private final Map<String, TaskFactory> factoriesMap = new HashMap<>();

void registerFactory(String identifier, TaskFactory factory) {
    factoriesMap.put(identifier, factory);
}

public Task create(String identifier) {
    return factoriesMap.get(identifier).create();
}

Then we can register different kinds of factories using a lambda or something:

Processor processor = ...;
registerFactory("startprocessor", () -> new StartProcessingTask(processor));

etc.

At some point you're going to realize that your "factory map" is basically a kind of Service Locator, in which case you either need to double-down on that, or find an alternative solution. I tend to prefer Dependency Injection as an approach here. Depending on your DI environment, you might make all your TaskFactory instances injectable using qualifiers. You can either bind lazy providers of actual task objects, or bind a factory-like object (e.g. "assisted inject").

like image 96
Daniel Pryden Avatar answered Nov 14 '22 22:11

Daniel Pryden