How to design abstract listener and its implementation?

323 views Asked by At

I have decided to split my application into 3 separate modules - one "abstract" with almost all application logic (anyone looking at the code can tell WHAT it does), one "implementation" module with all specific implementation layers (like db, connections, etc) and one small "runtime" module that maps implementation with abstraction (configures factories etc) and starts the app.

So I have an abstract PortListener class that can handle specific messages recevied from port. Most of the logic is done in this class, only specific implementation details (like port opening, closing, reading bytes from port and converting it to domain message) are moved to the PortListenerImplementation. PortListener have abstract methods called "listen()" - it should open port and add itself as a listener to the port, and "close()" - remove listener, close port. And there is method called "handleMessage(Message message)" that handles messages and do whatever it is needed.

How to obligate PortListenerImplementation to call "handleMessage()" in its own "real" listener method? Is there something like Reversed Template Method? Or maybe I've just gone too far? :D

Thx

1

There are 1 answers

0
user1071076 On BEST ANSWER

diagram

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.