Diagram UML do programu

0

Mam napisany kod na temat rezerwacji pokoi w Hotelu, mamy klasy osoba, gosc, pokoj, rezerwacja i hotel. I moje pytanie brzmi czy ten diagram UML jest odpowiedni dla tego kodu? Proszę o pomoc, strasznie długo nad tym siedzę i główkuje, ponieważ mam problem z diagramem i relacjami Rezerwacji, a muszę mieć 100% pewność.

#ifndef HOTEL_H
#define HOTEL_H
#include "Pokoj.h"
#include "Gosc.h"
#include "Rezerwacja.h"
#include <vector>
#include <map>
#include <string>
#include <stdexcept>

class Hotel {
private:
    std::vector<Pokoj> pokoje = {
        Pokoj(101, "Jednoosobowy"),
        Pokoj(102, "Dwuosobowy"),
        Pokoj(103, "Apartament"),
        Pokoj(104, "Jednoosobowy"),
        Pokoj(105, "Dwuosobowy")
    };
    std::vector<Gosc> goscie;
    std::map<int, Rezerwacja> rezerwacje;

public:
    void dodajGoscia(const Gosc& gosc);
    bool czyIdGosciaJestUnikalne(int idGoscia) const;
    bool dokonajRezerwacji(int numerPokoju, int idGoscia, const std::string& dataRozpoczecia, const std::string& dataZakonczenia);
    bool anulujRezerwacje(int idGoscia);
    Pokoj* znajdzPokoj(int numerPokoju);
    Gosc* znajdzGoscia(int idGoscia);
    std::vector<Pokoj> pobierzDostepnePokoje(const std::string& dataRozpoczecia, const std::string& dataZakonczenia) const;

    bool zapiszGosciDoPliku(const std::string& nazwaPliku);
    bool wczytajGosciZPliku(const std::string& nazwaPliku);
    bool zapiszRezerwacjeDoPliku(const std::string& nazwaPliku);
    bool wczytajRezerwacjeZPliku(const std::string& nazwaPliku);

    const std::map<int, Rezerwacja>& pobierzRezerwacje() const { return rezerwacje; }
}
#endif // HOTEL_H


#ifndef GOSC_H
#define GOSC_H
#include "Osoba.h"

class Gosc : public Osoba {
private:
    int idGoscia;

public:
    Gosc() : Osoba("", ""), idGoscia(0) {}
    Gosc(const std::string& imie, const std::string& nazwisko, int id);

    virtual void wyswietlInformacje() const override;
    int pobierzIdGoscia() const;
    std::string pobierzImie() const;       // getter for imie
    std::string pobierzNazwisko() const;   // getter for nazwisko
#endif // GOSC_H


#ifndef OSOBA_H
#define OSOBA_H
#include <string>

class Osoba {
protected:
    std::string imie;
    std::string nazwisko;

public:
    Osoba(const std::string& imie, const std::string& nazwisko);

    virtual void wyswietlInformacje() const = 0; // Metoda czysto wirtualna
};
#endif // OSOBA_H


#ifndef POKOJ_H
#define POKOJ_H
#include <iostream>
#include <string>

class Pokoj {
private:
    int numerPokoju;
    std::string typ;
    bool status; // true = dostępny, false = zarezerwowany

public:
    Pokoj() : numerPokoju(0), typ(""), status(true) {}
    Pokoj(int numer, const std::string& typ);

    int pobierzNumerPokoju() const;
    std::string pobierzTyp() const;
    bool pobierzStatus() const;
    void ustawStatus(bool nowyStatus);
    bool operator==(const Pokoj& other) const;
    friend std::ostream& operator<<(std::ostream& os, const Pokoj& pokoj);
};
#endif // POKOJ_H


#ifndef REZERWACJA_H
#define REZERWACJA_H
#include "Pokoj.h"
#include "Gosc.h"
#include <string>

class Rezerwacja {
private:
    Pokoj pokoj;
    Gosc gosc;
    std::string dataRozpoczecia;
    std::string dataZakonczenia;
    bool anulowana; 

public:
    Rezerwacja() : anulowana(false) {}
    Rezerwacja(const Pokoj& pokoj, const Gosc& gosc, const std::string& dataRozpoczecia, const std::string& dataZakonczenia);
    
    void wyswietlInformacje() const;
    int pobierzNumerPokoju() const;
    int pobierzIdGoscia() const;
    std::string pobierzDataRozpoczecia() const;
    std::string pobierzDataZakonczenia() const;
    void ustawStatusAnulowanej();
    bool czyAnulowana() const;
}
#endif // REZERWACJA_H

Stworzyłem taki diagram, ale niestety nie jestem pewny co do niego i proszę o pomoc

obraz_2024-07-03_211833532.pngHOTEL
GOSC-------------kompozycja------

5

po samym patrzeniu na ten rysunek powinienieś odnieść wrażenie jak wiele rzeczy jest tu nie tak. Dajmy parę przykładów na rozgrzewkę:

  1. Po co Rezerwacja trzyma cały obiekt Pokój? Wystarczy jego id.
  2. To samo gość. I pomyśl co się wtedy stanie z tymi wszystkimi strzałkami?
  3. daty, jak sama nazwa wskazuje należy użyc typu z std::chrono.
  4. Anulowana. A nie lepiej status na jakiś enum? Nie znam zadania ale dziwne to trochę.
  5. W ogóle po co pokój jest klasą? Niech to będzie struct
  6. Podobnie osoba.
  7. Czasami pokój nie powinine mieć pola zarezerowany a nie jakaś magiczna mapa?
  8. Zauważ też jak zanieczyszczone masz api Hotel. Np. Ta klasa obsługuje zapis do pliku.

Dużo poprawek przed tobą. A ja tylko rzuciłem na to okiem.

0

Dziękuję za słuszne uwagi, które w przyszłości będę starał się zastosować.
Jednak na ten moment zależy mi bardziej na tym, że jest dany kod i czy diagram jest poprawnie zrobiony do tego kodu.
Byłbym wdzięczny aby w tej sytuacji nie patrzeć na błędy logiczne w kodzie, tylko czy diagram jest poprawnie wykonany, a przede wszystkim jego relacje między klasami.

1
damian2002 napisał(a):

I moje pytanie brzmi czy ten diagram UML jest odpowiedni dla tego kodu?

Zamiast się zastanawiać, to lepiej skorzystać z jakiegoś generatora UML-ów.

1

to mi wypluł chatgpt, i chyba to jest to co twój wykładowca chciał, przynajmniej po tym co napisałeś markowi w kwestii zawierania. Problem z uml jest taki że znakomita większość z nas go nie używa albo wybiórczy sposób i cięzko pomóc.
screenshot-20240705170411.png

0
revcorey napisał(a):

to mi wypluł chatgpt, i chyba to jest to co twój wykładowca chciał, przynajmniej po tym co napisałeś markowi w kwestii zawierania. Problem z uml jest taki że znakomita większość z nas go nie używa albo wybiórczy sposób i cięzko pomóc.

Właśnie długo się nad tym zastanawiałem, lecz w instrukcji o agregacji całkowitej (w instrukcji kompozycja) mamy napisane tak:
screenshot-20240705201034.png

Jak to czytałem to jakoś zinterpretowałem to, że kompozycja od klasy może być tylko raz, a w moim przypadku jestem w 100% pewny, ze Pokoj i Gosc maja kompozycje do Hotelu. I teraz pytanie, czy źle zinterpretowałem, ze obiekt nie może należeć do wielu obiektów i ciągle omijałem ten odpowiedniej relacji?

0

Dla ustalenia uwagi:

  • Linia z pustym diamentem - agregacja (worek na obiekty, co do których niekoniecznie mamy prawo własności i zarządzania ich cyklem życia)
  • Linia z pełnym diamentem - kompozycja (worek na obiekty, ale jesteśmy ich właścicielem i zarządzamy cyklem życia)
  • Linia - asocjacja (strzałką pokazuje kierunek, jedna klasa wie o drugiej, ale niekoniecznie na odwrót).
Rezerwacja(const Pokoj& pokoj, const Gosc& gosc, const std::string& dataRozpoczecia, const std::string& dataZakonczenia);

...
private:
    Pokoj pokoj;
    Gosc gosc;

Tworząc rezerwację, przekazujesz parametry przez referencje. Zgaduję, że masz inicjalizację na zasadzie this->pokoj = pokoj;, czyli odpala się konstruktor kopiujący klasy Pokoj (nie zdefiniowałeś, ale kompilator dodał od siebie). W efekcie obiekt klasy Rezerwacja tworzy nowe obiekty gosc, pokoj, których czas życia nie przekracza czasu życia obiektu Rezerwacja. Innymi słowy Rezerwacja jest panem i władcą tych obiektów -> kompozycja -> linie z pełnym diamentem od Rezerwacja do Pokoj/Gosc.

Gdybyś w kodzie Rezerwacji miał coś innego, np.

private:
    Pokoj& pokoj;
    Gosc& gosc;

albo

private:
    Pokoj *pokoj;
    Gosc *gosc;

W tym przypadku nie powołujesz nowych obiektów, ani nie zarządzasz ich cyklem życia (powstały zanim powstał obiekt klasy Rezerwacja), więc jedynie zapamiętujesz "uchwyt" do nich. Tu mamy agregację, czyli od Rezerwacja do Pokoj/Gosc linie z pustym diamentem.

Moim zdaniem użycie asocjacji (linii zakończonej strzałką) między Rezerwacja i Pokoj/Gosc nie jest błędem. Bo jest to ogólniejszy koncept niż kompozycja czy agregacja i pokazuje, że między Rezerwacja a pozostałymi klasami jest jakiś związek i tyle. Na upartego mógłbyś wywalić te wszystkie diamenty i zastąpić strzałkami i bronić na zasadzie "to szczegół implantacyjny, który może ulec zmianie" ;-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1