0

The program I am working on has a distributed architecture, more precisely the Broker-Agent Pattern. The broker will send messages to its corresponding agent in order to tell the agent to execute a task. Each message sent contains the target task information(the task name, configuration properties needed for the task to perform etc.). In my code, each task in the agent side is implemente in a seperate class. Like :

public class Task1 {}
public class Task2 {}
public class Task3 {}
...

Messages are in JSON format like:

{
  "taskName": "Task1",  // put the class name here
  "config": {

  }
}

So what I need is to associate the message sent from the broker with the right task in the agent side.

I know one way is to put the target task class name in the message so that the agent is able to create an instance of that task class by the task name extracted from the message using reflections, like:

Class.forName(className).getConstructor(String.class).newInstance(arg);

I want to know what is the best practice to implement this association. The number of tasks is growing and I think to write string is easy to make mistakes and not easy to maintain.

Grim
  • 1,938
  • 10
  • 56
  • 123
qingl97
  • 327
  • 4
  • 14

1 Answers1

1

If you're that specific about classnames you could even think about serializing task objects and sending them directly. That's probably simpler than your reflection approach (though even tighter coupled).

But usually you don't want that kind of coupling between Broker and Agent. A broker needs to know which task types there are and how to describe the task in a way that everybody understands (like in JSON). It doesn't / shouldn't know how the Agent implements the task. Or even in which language the Agent is written. (That doesn't mean that it's a bad idea to define task names in a place that is common to both code bases)

So you're left with finding a good way to construct objects (or call methods) inside your agent based on some string. And the common solution for that is some form of factory pattern like: http://alvinalexander.com/java/java-factory-pattern-example - also helpful: a Map<String, Factory> like

interface Task {
    void doSomething();
}

interface Factory {
    Task makeTask(String taskDescription);
}

Map<String, Factory> taskMap = new HashMap<>();

void init() {
    taskMap.put("sayHello", new Factory() {
        @Override
        public Task makeTask(String taskDescription) {
            return new Task() {
                @Override
                public void doSomething() {
                    System.out.println("Hello" + taskDescription);
                }
            };
        }
    });
}

void onTask(String taskName, String taskDescription) {
    Factory factory = taskMap.get(taskName);
    if (factory == null) {
        System.out.println("Unknown task: " + taskName);
    }
    Task task = factory.makeTask(taskDescription);

    // execute task somewhere
    new Thread(task::doSomething).start();
}

http://ideone.com/We5FZk

And if you want it fancy consider annotation based reflection magic. Depends on how many task classes there are. The more the more effort to put into an automagic solution that hides the complexity from you.

For example above Map could be filled automatically by adding some class path scanning for classes of the right type with some annotation that holds the string(s). Or you could let some DI framework inject all the things that need to go into the map. DI in larger projects usually solves those kinds of issues really well: https://softwareengineering.stackexchange.com/questions/188030/how-to-use-dependency-injection-in-conjunction-with-the-factory-pattern

And besides writing your own distribution system you can probably use existing ones. (And reuse rather then reinvent is a best practice). Maybe http://www.typesafe.com/activator/template/akka-distributed-workers or more general http://twitter.github.io/finagle/ work in your context. But there are way too many other open source distributed things that cover different aspects to name all the interesting ones.

Community
  • 1
  • 1
zapl
  • 63,179
  • 10
  • 123
  • 154
  • Very informative answer! Clear code example and good recommended articles! The native factory solution is simple enough which I prefer. I have about 4 types of tasks where each type shares a same task configuration. Within each type, there are about 4 to 5 exact tasks. So the total number of tasks is relatively big. – qingl97 Oct 08 '15 at 22:29