c++ OOP + UML



  • Hallo Leute,

    ich wende mich an euch, da ich so langsam einer Depression nahe bin 😉

    An der Uni hatten wir vor einigen Semestern flüchtig OOP Entwicklung mit Java. Bisher hab ich relativ viel in PHP strukturiert (also prozedural mit funktionen zur Wiederverwendung) programmiert.

    Der OOP-Gedanke will mir anscheinend noch nicht so ganz in den Kopf. Ich male grade zum Training UML Diagramme und habe mir als Lernprojekt jetzt mal eine Kundenverwaltung vorgenommen.

    Beim zeichnen der Diagramme hat sich mir folgende Frage gestellt:

    Gesetzt den Fall wir haben eine Klassenstruktur (nur ein kleiner Ausschnitt zum aufzeigen des Verständnisproblems), die vereinfacht so aussieht (ich hab das ganze jetzt mal je nach zugehörigem "modul" in einzelne packages gesetzt, tut "man" das so?)

    Package: CRM (customer relationship management)

    Enthaltene Klassen:
    Kunde (attribute u.a.: kundennummer, name, strasse, plz,.......)
    -> Privaktunde
    -> Geschäftskunde (jeweils von Kunde abgeleitet mit neuen attributen wie Umsatzsteuerid etc.)

    Package: Billing (Rechnungs- und Vertragsverwaltung)

    Enthaltene Klassen:
    Invoice (rechnung mit rechnungsnummer etc.)
    InvoiceItem (rechnungsposten)
    ...

    Jetzt zum Problem:

    Ein Kunde kann viele Rechnungen haben, jede Rechnung kann nur zu einem Kunden gehören.

    Hat meine Rechnung - damit ich sie dem Kunden zuordnen kann - ein Attribut vom Typ der Klasse Kunde, oder hat sie nur ein Attribut vom typ integer, in dem die Kundennummer gespeichert wird?

    In der Mysql-DB muss ich ja - um das beim nächsten Programmstart wiederherstellen zu können - ohnehin die kundennummer abspeichern.

    Und die nächste Frage:

    Wenn ich jetzt bspw. einen Programmteil hab, der alle fälligen Rechnungen erzeugt... (Also die Rechnungen instanziiert, sie passend parametriert und evtl. eine methode der Klasse rechnung aufruft, die die Rechnung in die Mysql-Db sichert)

    Wer tut das?! brauch ich eine Klasse "Rechnungsverwaltung", die die Methode "erstelle fällige rechnungen" implementiert?

    Ihr seht schon, ein an prozedurale Programmierung gewöhnter stirbt grad den Herztod...

    Danke euch (schon nur fürs durchlesen)...

    -Tom



  • sorci schrieb:

    Jetzt zum Problem:

    Ein Kunde kann viele Rechnungen haben, jede Rechnung kann nur zu einem Kunden gehören.

    Hat meine Rechnung - damit ich sie dem Kunden zuordnen kann - ein Attribut vom Typ der Klasse Kunde, oder hat sie nur ein Attribut vom typ integer, in dem die Kundennummer gespeichert wird?

    Das ist deine Designentscheidung. Entweder versucht man Kopplungen möglichst zu vermeiden (den Weg beschreite ich üblicherweise wenn sich Objekte in anderen physikalischen oder teilweise auch logischen Einheiten befinden, z.b. über DLL-Grenzen hinweg), der zweite Ansatz erleichtert wiederum die Verwendung.

    Ergänzung:
    In einer Anwendung versuche ich weitgehend mit zwei Werten über Schnittstellen hinweg auszukommen: Einer TypId und einer DatensatzId. Die DLLs werden beim Start geprüft welche Typen sie behandeln können, und ggf. in welcher Priorität [Eine Art Plugin-System]. Wenn ich z.B. eine Adresse habe kann es je nach DLL sein, das bei einer Bearbeitung eine einfache, oder eine komplexe Adressmaske verwendet wird.
    Nachteil ist natürlich das die Programmierung erschwert und die Fehleranfälligkeit erhöht wird (Man muß prüfen ob überhaupt für einen Typ eine Behandlung existiert, TypIds sind nicht "Typsicher"...)...

    sorci schrieb:

    Wenn ich jetzt bspw. einen Programmteil hab, der alle fälligen Rechnungen erzeugt...

    Wer tut das?! brauch ich eine Klasse "Rechnungsverwaltung", die die Methode "erstelle fällige rechnungen" implementiert?

    Hört sich für mich nicht verkehrt an. Man kann durchaus "Abläufe" als Klasse betrachten (Wobei man dies vielleicht noch weiter aufsplitten könnte - ziehe ich in der Regel vor, spätestens wenn Klassen recht groß werden - und eine Klasse Rechnungsstellung nimmt).

    cu André



  • Es gibt sicherlich mehrere Möglichkeiten.

    1. gib der Invoice-Klasse eine static-Funktion ala
    std::list<Invoice*> Invoice::invoicesForKundenID(int id);
    

    Die zu der passenden Kunden-ID alle Rechnungen in einer Liste zurückgibt.

    1. Verpass deiner "Kunden"-Klasse eine Liste mit allen Rechnungen als Member.

    2. Wenn es wirklich viele Kunden werden, kann der Speicherverbrauch horrend hoch werden, wenn immer alle Kunden und Rechnungen im Speicher liegen. Da bietet es sich vllt. an, wenn du eine Zentrale DB-Manager-Klasse hast, die auf Anfrage bestimmte Kunden, bzw. Rechnungen ausspuckt. Die Anfragen resultieren alle in nem DB-Query.
      Eine DBManager-Klasse hat auch den Vorteil, dass du nur in ener Klasse den Zugriff auf die DB implementieren musst. Wenn also später neue komponenten hinzu kommen oder geändert werden, musst du nicht in zig Dateien rumwerkeln, um den Code anzupassen.

    Prinzipiell ist die Kunden-ID ja die Komponente, welche zur eindeutigen Identifizierung eines Kunden-Instanz herangezogen wird. Wenn du also die Rechnungen nicht bereits sortiert nach Kunde im Speicher hältst, sollte es reichen, wenn du eine Methode mit der KundenID als Parameter zur Verfügung stellst, um an eben jene Rechnungen zu kommen.

    Grüße
    Franz


Anmelden zum Antworten