Я работаю над проектом, который включает связь между компьютерным приложением и встроенными устройствами через последовательный порт в режиме Master-Slave.
Приложение будет служить мастером для нескольких встроенных устройств, работающих как подчиненные.
Коммуникационная часть почти завершена. Но теперь я реорганизую его как API. Таким образом, он может использоваться для нескольких проектов или для многих разработчиков с очень небольшими конфигурациями.
Я не очень хорошо разбираюсь в дизайне API, даже в первый раз я создаю API.
Теперь я застрял в следующем выпуске: рассмотрим этот сценарий:
/*
* 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
запускает рабочий поток для работы с запросом и ответом, так что длительные операции не могут вызвать потерю предстоящих ответов от ведомых.
Теперь, вот "часть использования API":
/*
* 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.
}
}
Но, как мы видим, AbstractSlave
создает экземпляры SlaveWorker
. Таким образом, будет вызван метод SlaveWorker
work()
вместо MyWorker
.
Как сделать класс AbstractSlave
для вызова MyWorker
work()
MyWorker
?
ЗАМЕТКА:
AbstractSlave
не знал бы, есть класс MyWorker
. Таким образом, экземпляры MyWorker
не могут быть созданы непосредственно вместо SlaveWorker
.handle()
AbstractSlave
может/не может быть переопределен, потому что есть некоторые операции, которые необходимо выполнить перед началом рабочего потока. Я думаю, что ключевым моментом было бы позволить клиенту вашего API создать экземпляр SlaveWorker
(или любого подкласса), чтобы он мог настроить метод work()
.
IMO вы должны предоставить интерфейс Worker
в вашем API (интерфейс менее сдерживает, чем абстрактный класс):
public interface Worker {
public void work(Request request, Response response);
}
Метод AbstractSlave
должен выглядеть следующим образом:
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();
}
}
Существуют разные способы сделать это, но один из способов - добавить метод configureJob
к вашему AbstractSlave
и использовать его, чтобы рассказать класс AbstractSlave
о 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);
}
В своем main
методе просто вызовите SlaveManager::configureJob(MyWorker.class)
прежде чем вы SlaveManager::handle()
.
Теперь, я сохранил вещи простыми выше, используя Object.newInstance()
для создания Worker
, но это не рекомендуемая общая практика. WorkerFactory
этого было принято использовать WorkerFactory
, но я не хотел вводить новый класс и новый шаблон дизайна, если вы не знакомы с Factory Pattern.
Worker
больше не является Thread
, вы не можете вызвать start()
. Кроме того, я не уверен, что OP хочет, чтобы клиент мог изменить поведение work()
в любое время, но это должен сказать @Akshat;)