Masalah desain OOP. Dua jenis Opsional kosong

8

Saya sedang menulis aplikasi yang cukup sederhana yang berkaitan dengan pemesanan kamar hotel. Saya punya masalah pada satu tahap.

Saya sedang memproses antrian pesanan. Untuk setiap pesanan, salah satu resepsionis harus memilih kamar (satu atau tidak sama sekali) untuk klien sesuai dengan strateginya. Itu sebabnya saya memutuskan untuk pergi dengan Java Optional. Masalahnya adalah bahwa jika tidak ada kamar gratis pada tanggal yang diinginkan pesanan harus dibatalkan, tetapi jika ada beberapa kamar yang tersedia dan tidak ada yang sesuai dengan strategi resepsionis bahwa pesanan harus dikembalikan ke antrian.

Memilih kamar tentu harus menjadi tugas resepsionis. Menurut Anda apa cara terbaik untuk mengatasi masalah itu dengan cara yang bersih? Haruskah saya melemparkan pengecualian daripada mengembalikan kosong Optionalketika tidak ada kamar di tanggal? Sayangnya, pengecualian biasanya bukan solusi yang baik untuk mengendalikan aliran kode.

Fragmen kode:

    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
sumber
1
Apa gunanya mengembalikan pesanan dalam antrian? Bukankah penerimaan akan gagal berulang kali?
Winston Ewert
@ WinstonEwert No. Ada banyak resepsionis yang berurusan dengan pesanan dan mereka punya strategi pemilihan yang berbeda.
Paweł Koniarski
@ WinstonEwert Saya akan berasumsi sampai keadaan hotel berubah.
candied_orange
@ PawełKoniarski tolong jelaskan 'strategi' dalam konteks ini
candied_orange
1
Yap, persis seperti itu. Resepsionis dilingkari dalam siklus dan setiap pesanan berikutnya dilayani oleh resepsionis lainnya.
Paweł Koniarski

Jawaban:

1

Saya pikir Anda bisa memodelkannya dalam dua cara:

Opsi 1: Menggunakan pembungkus + enum untuk respons resepsionis:

enum ReceptionistDecision {
    BOOK_ROOM,
    NO_ROOM,
    RETURN_TO_QUEUE,
}

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

    ReceptionistResponse(Room room) {
        ...
    }

    ReceptionistResponse(ReceptionistDecision decision) {
        ...
    }
}

Opsi 2: Atau Anda bisa kelas antarmuka, dan membuat masing-masing respons mewarisi darinya. Sesuatu seperti:

interface class ReceptionistResponse {

}

class ReturnToQueueReceptionistResponse implements ReceptionistResponse {

}

class NoRoomsBookedQueueReceptionistResponse implements ReceptionistResponse {

}

class RoomBookedReceptionistResponse implements ReceptionistResponse {
    Room BookedRoom;
}

The chooseRoomMetode akan menjadi:

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

Dan kode klien untuk opsi 1:

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

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

    orders.add(order);
}
RMalke
sumber
Saya telah memutuskan untuk pergi dengan sesuatu yang dekat dengan opsi pertama Anda. Terima kasih banyak atas bantuan Anda.
Paweł Koniarski
5

Ada beberapa pendekatan yang bisa diterapkan untuk pemodelan ini.

Pertama, kita mungkin memiliki resepsionis pasif. Resepsionis pasif memutuskan apa yang harus dilakukan, tetapi tidak melakukan apa-apa. Sebagai gantinya, kami memiliki kelas seperti

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

Sekarang, Anda mungkin memperhatikan bahwa ini adalah ide dasar yang sama dengan opsional, tetapi kami telah memperluasnya menjadi tiga opsi, bukan hanya satu. Setiap metode statis membuat dan mengembalikan respons tertentu. Kemudian resepsionis Anda memiliki metode

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

Kode panggilan mengambil ReceptionistResponse dan melakukan respons yang diperlukan.

Atau, Anda mungkin memiliki resepsionis yang aktif. Resepsionis aktif sebenarnya melakukan tindakan:

void attemptBookOrder(Order order) {
    if (rooms.allRoomsAreFull()) {
       order.cancel();
    }
    if (...) {
       order.bookRoom(room);
    } else {
       orderQueue.put(order);
    }
}
Winston Ewert
sumber
Bagaimana Anda menyusun Respons Receptionist untuk menangani tiga kemungkinan tanggapan? Enum plus bidang Kamar opsional? Bagaimana dengan kasus di mana tanggapan yang berbeda masing-masing memiliki banyak properti (mis. Persen cocok atau kamar tersedia)?
AmadeusDrZaius
1
@AmadeusDrZaius, Anda bisa menggunakan enum dengan opsional, atau beberapa subclass.
Winston Ewert
1

Mengembalikan Opsional tampaknya OK tetapi jika tidak ada nilai maka logika tidak boleh dilanjutkan. Setiap tamu memiliki ruang nyata yang ditugaskan atau bukan tamu. Jadi, begitu resepsionis memutuskan untuk mengembalikan Opsional kosong, pesanan harus dimasukkan kembali dalam antrian dan tidak lebih.

Tidak apa-apa untuk pertama-tama mengambil pesanan dari antrian, kemudian melakukan resepsionis. Pilih saja dan jika itu kembali kosong, tambahkan kembali pesanan ke (akhir) antrian. Saya akan membungkusnya dalam percobaan-akhirnya untuk memastikan tidak ada pesanan yang hilang.

Jika suatu ruangan dipilih, itu harus dilanjutkan sebagai ruangan, bukan opsional. Opsional harus digunakan sebagai variabel sementara hanya karena satu-satunya tujuan adalah untuk mendeteksi bahwa resepsionis memutuskan untuk tidak menangani pesanan untuk saat ini.

Martin Maat
sumber