Vincas Stonys Vincas Stonys - 4 days ago 4
Java Question

Observer pattern: notify with state

The examples I've seen on the internet only give

notifyObservers()
method to the Observable object. What if I want to also pass some data to the observers, so that they know if they should react to it or not? Does the observer pattern allow passing the data like maybe:

notifyObservers(Event e)
.

Or maybe the observers themselves should pull the change? Like maybe:
notifyObservers() -> call observer.update()


and then each observer decides if the new data is relevant for them,
observable.getData()
. However with this approach, it is unclear to me, how to get only the data that has changes, if it's at all needed.

Edit, example:

Say I want to make a taxi firm with operators, clients and taxi. As per @Pritam Banerjee's answer, I will say that taxi firm is a mediator between operators and clients (e.g. why - client can call for a taxi by phone, or online). Then my operators are subjects, and taxis are observers.

Operator ---observes---> TaxiFirm


TaxiFirm(waits for client) ---notifies one---> Operator
(firm selects which operator is responsible, and passes a client to operator)

Taxi ---observes all---> Operators
(need to somehow push data here, if the taxi is occupied, it can't accept new client)

Taxi ---notifies---> Operator
(if taxi can accept a client it will notify operator about it, I am not concerned about any race conditions, because this event will be triggered manually. Also, maybe Taxi should notify the Firm and not operator?)

I think it's possible that TaxiFirm doesn't need to pass client to operator, but thinking about real life, it's really operators who speak to clients...

I've written down my thought process, please help me with figuring out the architecture for this model.

Answer

Of course the ObserverPattern allows you to pass information through its notify method. If a taxi needs the client info you can just pass:

observer.notify(ClientInfo info)

And of course the Observers could, instead, request the info:

observer.notify()

void notify() {
    update();
}

Both are possible, but then, I would NOT say you really have the ObserverPattern here. According to this pattern, the Subject simply notifies all Observers. But what you described is that the Subject should notify one of the taxis, wait for its response (if the taxi is already carrying a passenger), and then possibly notify the next taxi. You could call that a variation of the ObserverPattern, but it's different.

A simple suggestion, for you to get started:

class Operator:

List<Taxi> taxis;

boolean chooseTaxi(RideNumber rideNumber) {
    for (Taxi taxi : taxis) {
        boolean isAccepted = taxi.notify(this, rideNumber);
        if (isAccepted) {
           markTaxiRideAsAccepted(taxi, rideNumber);
           return true;
        }
    }
    return false; // No taxi accepted the ride.
}

class Taxi:

boolean notify(Operator operator, RideNumber rideNumber) {
    if (isTaxiAlreadyCarryingPassenger()) return false;

    ClientInfo clientInfo = operator.getClientInfo(this, rideNumber);
    startClientProcess(clientInfo);

    return true;
}

Note: The RideNumber is just an identification number that the taxi later uses to request the client info from the operator. You could instead send the clientInfo through the notify method, but then all taxis would get this info, which is terrible for security, and could also be confusing (sending information which should not be used).


Update:

If this is a homework assignment, and you must use the exact ObserverPattern, you can do this:

class Operator:

List<Taxi> taxis;

void notifyAllTaxis(RideNumber rideNumber) {
    for (Taxi taxi : taxis) {
        taxi.notify(this, rideNumber);            
        }
    }        
}

ClientInfo getClientInfo(this, rideNumber) {
    if (isRideNotYetAccepted(rideNumber)) {
        markRideAsAccepted(taxi, rideNumber);
        return getClientInfo(rideNumber);
    }
    else return null;
}

class Taxi:

void notify(Operator operator, RideNumber rideNumber) {
    if (!isTaxiAlreadyCarryingPassenger()) {

        ClientInfo clientInfo = operator.getClientInfo(this, rideNumber);
        if (clientInfo != null) startClientProcess(clientInfo);
    }
}
Comments