Problema de design OOP. Dois tipos de opcional vazio

8

Estou escrevendo um aplicativo bastante simples que trata da reserva de quartos de hotel. Eu tenho um problema em um estágio.

Estou processando uma fila de pedidos. Para cada pedido, um dos recepcionistas deve escolher um quarto (um ou nenhum) para o cliente, de acordo com sua estratégia. Por isso decidi ir com Java Optional. O problema é que, se simplesmente não houver salas livres na data desejada, o pedido deverá ser cancelado, mas se houver algumas salas disponíveis e nenhuma delas se encaixar na estratégia da recepcionista, essa ordem deverá ser colocada de volta na fila.

A escolha dos quartos definitivamente deve ser um dever da recepcionista. Qual você acha que é a melhor maneira de lidar com esse problema de maneira limpa? Devo lançar uma exceção em vez de retornar vazio Optionalquando não há quartos na data? Infelizmente, as exceções geralmente não são uma boa solução para controlar o fluxo 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
fonte
1
Qual a vantagem de colocar o pedido de volta na fila? A recepção não falhará repetidamente?
Winston Ewert
@WinstonEwert Não. Existem muitas recepcionistas que lidam com pedidos e têm estratégias de escolha diferentes.
Paweł Koniarski
@WinstonEwert Eu diria que sim até o estado do hotel mudar.
candied_orange
@ PawełKoniarski defina 'estratégia' neste contexto
candied_orange
1
Sim, exatamente assim. Os recepcionistas são repetidos em um ciclo e cada próximo pedido é atendido por outro recepcionista.
Paweł Koniarski

Respostas:

1

Eu acho que você poderia modelá-lo de duas maneiras:

Opção 1: Usando um wrapper + enum para a resposta da recepcionista:

enum ReceptionistDecision {
    BOOK_ROOM,
    NO_ROOM,
    RETURN_TO_QUEUE,
}

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

    ReceptionistResponse(Room room) {
        ...
    }

    ReceptionistResponse(ReceptionistDecision decision) {
        ...
    }
}

Opção 2: Ou você pode criar uma classe de interface e fazer com que cada uma das respostas seja herdada. Algo como:

interface class ReceptionistResponse {

}

class ReturnToQueueReceptionistResponse implements ReceptionistResponse {

}

class NoRoomsBookedQueueReceptionistResponse implements ReceptionistResponse {

}

class RoomBookedReceptionistResponse implements ReceptionistResponse {
    Room BookedRoom;
}

O chooseRoommétodo seria:

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);
    }
}

E o código do cliente para a opção 1:

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

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

    orders.add(order);
}
RMalke
fonte
Eu decidi ir com algo próximo à sua primeira opção. Muito obrigado por sua ajuda.
Paweł Koniarski
5

Existem algumas abordagens que se poderia aplicar para modelar isso.

Primeiro, podemos ter a recepcionista passiva. A recepcionista passiva decide o que fazer, mas não faz nada. Em vez disso, temos classe algo como

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

Agora, você pode perceber que essa é a mesma ideia básica que a opcional, mas a ampliamos para três opções em vez de apenas uma. Cada método estático cria e retorna uma resposta específica. Então sua recepcionista tem um método

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

O código de chamada pega o ReceptionistResponse e realiza a resposta necessária.

Como alternativa, você pode ter uma recepcionista ativa. O recepcionista ativo realmente executa as ações:

void attemptBookOrder(Order order) {
    if (rooms.allRoomsAreFull()) {
       order.cancel();
    }
    if (...) {
       order.bookRoom(room);
    } else {
       orderQueue.put(order);
    }
}
Winston Ewert
fonte
Como você estruturaria o ReceptionistResponse para lidar com as três respostas possíveis? Um enum mais um campo opcional da sala? Que tal um caso em que as diferentes respostas tenham inúmeras propriedades (por exemplo, percentMatched ou roomsAvailable)?
AmadeusDrZaius
1
@AmadeusDrZaius, você pode usar o enum com subclasses opcionais ou múltiplas.
Winston Ewert 23/04
1

Retornar o Opcional parece OK, mas se não houver valor, a lógica não deve continuar. Cada hóspede tem uma sala real atribuída a ele ou não é um hóspede. Assim, quando a recepcionista decidir devolver um Opcional vazio, o pedido deverá ser colocado novamente na fila e nada mais.

Não há problema em primeiro receber o pedido da fila e, em seguida, executar o receptionist.chooseRoom e, se voltar vazio, adicione novamente o pedido ao final da fila. Eu o encerraria em uma tentativa - finalmente, para garantir que nenhum pedido seja perdido.

Se uma sala for selecionada, ela deverá prosseguir como uma sala, não como opcional. Opcional deve ser usado como uma variável temporária apenas porque seu único objetivo é detectar que a recepcionista decidiu não lidar com o pedido no momento.

Martin Maat
fonte