OOP problema de diseño. Dos tipos de vacío Opcional

8

Estoy escribiendo una aplicación bastante simple que se ocupa de la reserva de habitaciones de hotel. Tengo un problema en una etapa.

Estoy procesando una cola de pedidos. Para cada pedido, uno de los recepcionistas debe elegir una habitación (una o ninguna) para el cliente de acuerdo con su estrategia. Es por eso que decidí ir con Java Optional. El problema es que si simplemente no hay habitaciones libres en la fecha deseada, el pedido debe cancelarse, pero si hay algunas habitaciones disponibles y ninguna de ellas se ajusta a la estrategia de la recepcionista, ese pedido debe volver a la cola.

La elección de las habitaciones definitivamente debería ser un deber de recepcionista. ¿Cuál crees que es la mejor manera de lidiar con ese problema de manera limpia? ¿Debo lanzar una excepción en lugar de volver vacía Optionalcuando no hay habitaciones en la fecha? Desafortunadamente, las excepciones no suelen ser una buena solución para controlar el flujo de código.

Fragmento de código:

    Optional<Room> selectedRoom = receptionist.chooseRoom(rooms, 
                                                          order.getQuestionnaire());

    boolean decision = selectedRoom
            .map(room -> receptionist.askClient(order.getClient(), 
                                                room,
                                                order.getQuestionnaire()))
            .orElse(false);

    if (shouldProcessAgain(order, selectedRoom.isPresent(), decision)) {
        orders.add(order);
    }
Paweł Koniarski
fuente
1
¿De qué sirve volver a poner la orden en la cola? ¿La recepción no fallará una y otra vez?
Winston Ewert
@ WinstonEwert No. Hay muchas recepcionistas que se encargan de los pedidos y tienen diferentes estrategias de elección.
Paweł Koniarski el
@ WinstonEwert Supongo que sí hasta que cambie el estado del hotel.
candied_orange
@ PawełKoniarski por favor defina 'estrategia' en este contexto
candied_orange
1
Sí, exactamente así. Los recepcionistas se colocan en un ciclo y cada próximo pedido es atendido por otra recepcionista.
Paweł Koniarski

Respuestas:

1

Creo que podrías modelarlo de dos maneras:

Opción 1: uso de un contenedor + enumeración para la respuesta de la recepcionista:

enum ReceptionistDecision {
    BOOK_ROOM,
    NO_ROOM,
    RETURN_TO_QUEUE,
}

class ReceptionistResponse {
    ReceptionistDecision Decision;
    Optional<Room> Room;

    ReceptionistResponse(Room room) {
        ...
    }

    ReceptionistResponse(ReceptionistDecision decision) {
        ...
    }
}

Opción 2: O podría una clase de interfaz y hacer que cada una de las respuestas herede de ella. Algo como:

interface class ReceptionistResponse {

}

class ReturnToQueueReceptionistResponse implements ReceptionistResponse {

}

class NoRoomsBookedQueueReceptionistResponse implements ReceptionistResponse {

}

class RoomBookedReceptionistResponse implements ReceptionistResponse {
    Room BookedRoom;
}

El chooseRoommétodo sería:

ReceptionistResponse chooseRoom(List<Rooms> allRooms, Questionnaire questionnaire) {
    if (/* all rooms are full */) {
        // Option 1
        return new ReceptionistResponse(ReceptionistDecision.RETURN_TO_QUEUE);

        // Option 2
        return new ReturnToQueueReceptionistResponse();
    }

    if (/* Choose no rooms */) {
        // Option 1
        return new ReceptionistResponse(ReceptionistDecision.NO_ROOM);

        // Option 2
        return new NoRoomsBookedQueueReceptionistResponse();
    }

    if (/* Choose some room */) {
        // Option 1
        return new ReceptionistResponse(choosenRoom);

        // Option 2
        return new RoomBookedReceptionistResponse(choosenRoom);
    }
}

Y el código del cliente para la opción 1:

ReceptionistResponse response = receptionist.chooseRoom (rooms, order.getQuestionnaire ());

// options 1
if (response.Decision == ReceptionistDecision.RETURN_TO_QUEUE) {
// option 2
if (response instanceof(ReturnToQueueReceptionistResponse)) {

    orders.add(order);
}
RMalke
fuente
He decidido ir con algo cercano a su primera opción. Muchas gracias por tu ayuda.
Paweł Koniarski
5

Hay un par de enfoques que uno podría aplicar para modelar esto.

Primero, podríamos tener la recepcionista pasiva. La recepcionista pasiva decide qué hacer, pero no hace nada. En cambio, tenemos clase como

public class ReceptionistResponse {
    public static ReceptionistResponse bookRoom(Room room);
    public static ReceptionistResponse cancelOrder();
    public static ReceptionistResponse returnToQueue();
}

Ahora, puede notar que esta es la misma idea básica que opcional, pero la hemos extendido a tres opciones en lugar de solo una. Cada método estático crea y devuelve una respuesta particular. Entonces tu recepcionista tiene un método

ReceptionistReponse attemptBookOrder(Order order) {
    ...
    return ReceptionistResponse.bookRoom(room);
    ...
}

El código de llamada toma la Respuesta del Recepcionista y realiza la respuesta necesaria.

Alternativamente, puede tener una recepcionista activa. La recepcionista activa realmente hace las acciones:

void attemptBookOrder(Order order) {
    if (rooms.allRoomsAreFull()) {
       order.cancel();
    }
    if (...) {
       order.bookRoom(room);
    } else {
       orderQueue.put(order);
    }
}
Winston Ewert
fuente
¿Cómo estructuraría la Respuesta Recepcionista para manejar las tres respuestas posibles? ¿Una enumeración más un campo de habitación opcional? ¿Qué pasa con un caso en el que las diferentes respuestas tienen numerosas propiedades (por ejemplo, porcentaje de coincidencia o habitaciones disponibles)?
AmadeusDrZaius
1
@AmadeusDrZaius, puede usar la enumeración con subclases opcionales o múltiples.
Winston Ewert
1

Devolver el Opcional parece estar bien, pero si no hay ningún valor, la lógica no debería continuar. Cada invitado tiene una habitación real asignada o no es un invitado. Entonces, una vez que la recepcionista decidió devolver un Opcional vacío, el pedido debe volver a ponerse en la cola y nada más.

Está bien tomar primero el pedido de la cola, luego realizar el receptionist.chooseRoom y si vuelve vacío, volver a agregar el pedido a (el final de) la cola. Lo envolvería en un intento, finalmente, para asegurarme de que no se pierdan órdenes.

Si se selecciona una habitación, debe proceder como una habitación, no como Opcional. Opcional debe usarse como una variable temporal solo porque su único propósito es detectar que la recepcionista decidió no manejar la orden por el momento.

Martin Maat
fuente