
PortListener is an abstract class. Methods listen(), close(), readMessage(), sendMessage() are abstract and should be implemented in the child class. Those methods should create low level operations like opening connection or reading bytes from port. I do not want to mix that kind of details in my base class - I want only business logic there - method onMessageReceived() it is my business logic. It creates specific response, save data to database, change other objects etc.
PortListenerImp is a concrete class. It implements all needed methods. I would love to "force" PortListenerImp to use a parent's onMessageReceived() whenever a new message is received.
I think there is no easy way of doing it without going to specific details of SerialPortListener in abstract PortListener class. What I have managed is to make it more obvious what method should be called in the implementation class. I have added Event parameter to the PortListener class and added onEvent() method. Also method readMessage should ask for the Event object. So with this design it is more clear that PortListenerImp should use onEvent as an event handler:
public abstract class PortListener<Event> {
private final String portName;
public abstract void listen();
public abstract void close();
abstract Message readMessage(Event event);
abstract void sendMessage(Message message);
public final void onEvent(Event event) {
Message message = readMessage(event);
onMessageReceived(message);
}
private void onMessageReceived(Message message) {
// some business logic here
}
}
public class SerialPortListener extends PortListener<SerialPortEvent> {
private final SerialPort serialPort;
@Override
public void listen() {
if(!tryToOpenPortAndSetParameters())
throw new PortOpenException(getPortName());
tryToAddListener();
}
private boolean tryToOpenPortAndSetParameters() {
// return tryToOpenPort() && setPortParameters();
}
@Override
public void close() {
if(!tryToRemoveListenerAndClosePort())
throw new PortCloseException(getPortName());
}
private boolean tryToRemoveListenerAndClosePort() {
// return tryToRemoveListener() && tryToClosePort();
}
@Override
void sendMessage(Message message) {
// send messsage
}
@Override
Message readMessage(SerialPortEvent serialPortEvent) {
if (serialPortEvent.getEventValue() > 0)
return tryToReadMessage();
return Message.EMPTY;
}
private void tryToAddListener() {
try {
serialPort.addEventListener(this::onEvent);
} catch (SerialPortException e) {
throw new PortOpenException(getPortName(), e.getMessage());
}
}
private Message tryToReadMessage() {
// read message
}
}
Please comment what you think about this design. Source code was minimized just to make it more clear.