Pergunta

I am working on a project that includes communication between computer application and embedded devices over serial port in Master-Slave mode.

The application will serve as Master to multiple embedded devices working as Slaves.

The communication part is almost complete. But now, I am refactoring it as an API. So, it can be used over multiple projects or by many developers with very less configurations.

I am not very good in API design, even it's the first time, I am creating an API.

Now, I am stuck on following issue: Consider this scenario:

/*
 * API Part
 */
public abstract class AbstractSlave {

  // Some fields, constructor and other methods.

  final void handle(Request request, Response response) {
    // Some operations before starting worker thread.

    SlaveWorker worker = new SlaveWorker(request, response);
    worker.start();
  }

}

public class SlaveWorker extends Thread {

  // Constructor

  @Override
  public final void run() {
    work(request, response);
  }

  public void work(Request request, Response response) {

  }

}

AbstractSlave class starts a worker thread to work upon the request and response, so that long-running operations cannot cause the loss of upcoming responses from slaves.

Now, here is the "API usage part":

/*
 * API Usage Part
 */
public class Slave extends AbstractSlave {

  // Constructor

}

public class MyWorker extends SlaveWorker {

  // Constructor

  @Override
  public void work(Request request, Response response) {
    super.work(request, response);

    // My work to be done upon request and response.
  }

}

But as we can see, AbstractSlave creates SlaveWorker instances. So, SlaveWorker work() method will be called, instead of MyWorker.

How to make AbstractSlave class to call MyWorker work() method?

NOTE:

  • As it's an API design, AbstractSlave would not know, there is a MyWorker class. So, MyWorker instances cannot be created directly in place of SlaveWorker.
  • handle() method of AbstractSlave can/meant not be overridden, because there are some operations, that need to be performed before starting worker thread.
Foi útil?

Solução

I think the key point would be to let the client of your API create the instance of SlaveWorker (or any subclass), so that he can customize the work() method.

IMO you should provide a Worker interface in your API (interface is less constraining than an abstract class):

public interface Worker {
    public void work(Request request, Response response);
}

And AbstractSlave's method should be like:

public abstract class AbstractSlave {

    private final Worker worker;

    public AbstractSlave(Worker worker) {
        this.worker = worker;
    }

    final void handle(final Request request, final Response response)
        // Some operations before starting worker thread.

        Thread t = new Thread() {
            public void run() {
                worker.work(request, response);
            }
        };
        t.start();
    }

}

Outras dicas

There are different ways to do this, but one way is to add a configureJob method to your AbstractSlaveand use this to tell your AbstractSlave class about MyWorker.

public class SlaveManager {
   private Class workerClass = SlaveWorker.class;

   public void configureJob(Class clazz){
      workerClass = clazz;
   }

   final void handle(Request request, Response response) {
      // Some operations before starting worker thread.

      Worker worker = workerClass.newInstance();
      worker.start(request, response);
   }
}

public interface Worker {
   public void work(Request request, Response response);
}

In your main method, just call SlaveManager::configureJob(MyWorker.class) before you call SlaveManager::handle().

Now, I've kept things simple above by using Object.newInstance() to create the Worker, but this is not a recommended general practice. It's more customary to use a WorkerFactory instead, but I didn't want to introduce a new class and a new design pattern in case you were unfamiliar with the Factory Pattern.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top