Visual C# 2012 Grundkurs

Struktur objektorientierter Systeme

Testen Sie unsere 2007 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
Die Struktur eines objektorientierten Systems besteht aus zwei unterschiedlichen Hierarchien. Diese unterteilen sich in die Klassenhierarchie, welche Vererbungslinien folgt, sowie die Objekthierarchie.

Transkript

Der folgende Abschnitt widmet sich der Struktur objektorientierter Systeme. Hier streifen wir ein kleines bisschen das Thema der Modellierung. In der Literatur wird es öfter so beschrieben, dass ein objektorientiertes System im Wesentlichen aus zwei Hierarchien besteht. Das eine ist die Klassenhierarchie, die den Vererbungslinien folgt, und das andere ist die Objekthierarchie. Die Klassenhierarchie, das ist so, wie wenn ich z. B. die Klasse "Kostenpunkt" habe und von ihr spezialisierte Klassen ableite wie "Tagespauschale" oder "Kilometerpauschale". Dann folgt die Klassenhierarchie diesen Vererbungslinien. Das möchte ich einmal anhand eines Diagramms zeigen. In manchen Versionen von Visual Studio können Sie ja Diagramme erzeugen. So ein Diagramm-Projekt möchte ich anlegen. In der Entwicklerwelt hat sich die Notation der UML durchgesetzt. Sie sollten also als Entwickler möglichst in der Lage sein, UML-Diagramme selbst zu erfassen und lesen zu lernen, also vor allem die Klasse-Diagramme und die Sequenz-Diagramme. Das sind zwei wirklich gute Werkzeuge mit Hilfe derer die Software-Entwickler ihre Ideen untereinander austauschen können. Ich lege hier das neue Klassen-Diagramm an, gebe dem Ganzen einen Namen, und habe jetzt hier ein leeres Diagramm im Visual Studio. Ich kann jetzt mit der Toolbox hier Elemente reinziehen. Dann möchte ich dieses Beispiel der Kostenpunkte zeigen. Ich habe hier die Klasse "Kostenpunkt" und von dieser Klasse "Kostenpunkt" leite ich jetzt weitere Klassen ab, wie z.B. die Klasse "Kilometerpauschale". Und dazu gibt es hier in der "Toolbox" diese Vererbungslinie Inheritance auf Englisch. Ich klicke also hier "Inheritance" an, dann habe ich hier dieses komische Cursor-Symbol und kann jetzt von "Kilometerpauschale" zu "Kostenpunkt" eine Linie ziehen, und siehe da, ich habe eine Vererbungslinie. In der UML-Notation ist das also eine Linie mit so einem hohlen Dreieck. Und weil es so schön war, nehmen wir noch eine weitere Klasse dazu, das wäre hier die "Tagespauschale", auch von "Kostenpunkt" abgeleitet ist. und ich sage, dass diese Tagespauschale Auf diese Weise erreichen wir verschiedene Hierarchien, und das sind dann die Ableitungshierarchien, also die Vererbungshierarchien. Und dem gegenüber stehen die Kompositionshierarchien, oder Objekthierarchien. Ich möchte das anhand von einem Bestellwesen zeigen. Sagen wir mal, ich hätte jetzt hier eine Klasse im System, das wäre die Klasse "Kunde". Und Kunden können "Bestellungen" abgeben. Also habe ich hier die Klasse "Bestellung". Und so eine Bestellung, die besteht dann aus "Bestellposten". Also nehme ich eine weitere Klasse dazu, "Bestellposten". Und jetzt ist es so: So eine Bestellung macht im System keinen Sinn ohne Ihren Kunden, der diese Bestellung aufgibt. Sie sind untrennbar miteinander verbunden. Ebenso ist es mit dem Bestellposten in Bezug auf die Bestellung. Stellen Sie sich mal vor, ich würde aus dem System eine Bestellung herauslöschen. Dann würde so ein Bestellposten für sich gesehen überhaupt keinen Sinn machen. Und immer, wenn so eine enge Beziehung da ist, so eine Ganzes-Teil Beziehung, dann sprechen wir von der Komposition. Und dann haben wir hier im UML-Diagramm die Möglichkeit, eine Kompositionslinie zu zeichnen. Und das machen wir jetzt. Und diese Kompositionslinie zeichnet sich in der UML dadurch aus, dass sie, von der Elternklasse ausgehend,  mit einer Raute anfängt. Von dieser Raute führt entweder ein Pfeil oder eine Linie zu der Kind-Klasse. Was macht den Unterschied zwischen Pfeil und Linie aus? Das ist einfach die Navigierbarkeit. Das heißt, wenn ich nur von "Kunde" nach "Bestellung" navigieren kann, also nur der Kunde seine Bestellungen kennt, dann muss ich so einen Pfeil hernehmen. Wenn aber die Bestellung auch den Kunden kennt, dann ist die Komposition nicht in beide Richtungen navigierbar, und da macht der Pfeil dann wenig Sinn. Und in dem Sinn kann man dann hier in den "Eigenschaften", bei dieser "Second Role" bei "Is navigable", "False" schreiben und dann ist der Pfeil weg. Das Nächste, was man hier angibt, ist die "Kardinalität". Es hat sich eingebürgert für eine Kardinalität, also dort, wo ein Kunde mehrere Bestellungen haben kann, einen Stern zu schreiben. Man schreibt entweder eine 1 oder eine 0..1 oder 0..5. Manchmal sieht man auch: 0..N oder 1..N, also für den Bereich oder einfach diese ganz kurze Schreibweise mit dem Stern, so dass man sagt: Ein Kunde kann eine oder mehrere Bestellungen haben. Entsprechend haben wir so eine Kompositionsbeziehung von Bestellung nach Bestellposten. Und auch hier ist es so: Eine Bestellung kann mehrere Bestellposten haben. Und man sollte schon von den Bestellposten zu den Bestellungen zurück navigieren können. Meistens macht die Rücknavigation in so objektorientierten Systemen Sinn, also machen wir das so. Ein Bestellposten, der besteht aus einer Anzahl und einem Produkt. Jetzt nehme ich also die Klasse "Produkt" hier mit rein, und jetzt gibt es eine andere Art an Beziehung zwischen Bestellposten und Produkt. Die Beziehung ist deswegen anders, weil, wenn ich einen Bestellposten löschen würde... Also angenommen, ich nehme eine Bestellung aus dem System, damit fallen auch alle Bestellposten aus dem System, weil die ohne die Bestellung keinen Sinn mehr machen würden. Ich würde also alle Bestellposten löschen, und jetzt wäre es natürlich Unsinn, die dazugehörigen Produkte zu löschen. Die sollen ja im System bleiben, die machen also eigenständig Sinn. Produktdaten gehören zu den Stammdaten eines Systems, das ist vielleicht auch noch ein gutes Unterscheidungsmerkmal, und können von daher für sich selbst stehen. Und immer dann, wenn eine Beziehung zu einer Klasse entsteht, die für sich selbst stehen kann, kann ich natürlich keine Komposition verwenden, sondern ich verwende eine Assoziation. Und das wäre in dem Fall diese Linie. Also: Der Bestellposten hat eine Assoziation zum Produkt, und hier würde es möglicherweise sogar wirklich Sinn machen, die Navigation nur in der Richtung von Bestellposten nach Produkt zu haben, weil man möglicherweise nicht so häufig wissen will, welche Bestellposten zu welchem Produkt gehören. Wenn man das braucht, ist es hier die gleiche Logik mit der Navigation, dass man eben bei dieser "Second role" dieses "Is navigable" wieder verstellt. Dadurch habe ich jetzt eine unterschiedliche Darstellung. Die Kardinalität in dem Fall bleibt auch "1", die ist nicht "N", sondern ich sage ja, zu einem Bestellposten gehört wirklich nur ein Produkt. Und innerhalb von so einer Beziehung auch umgekehrt. So ein Produkt ist ja nur einem Bestellposten in dieser Relation zugeordnet. Jetzt stellen wir uns einmal vor, in unserem System gäbe es so eine Art Katalog. Und der Katalog ist nach Kategorien aufgeteilt. Ein Online-Shop würde alle Produkte auflisten wollen, die zu so einer Kategorie gehören. Auch hier haben wir eine Assoziationsbeziehung. Wenn ich also jetzt die Klasse "Kategorie" mit reinnehme, dann hat diese Kategorie eine Assoziation zum Produkt. Wenn ich so eine Kategorie aus dem System entfernen würde, macht es noch lange keinen Sinn, alle Produkte aus dem System zu entfernen. Nur ist es so, dass einer Kategorie viele Produkte zugeordnet werden können, also machen wir hier auch wieder einen Stern. Und auch hier würde es durchaus Sinn machen, die Navigierbarkeit herauszunehmen, weil die Assoziation in beide Richtungen funktioniert, und es deswegen keinen Sinn macht, Richtungen anzugeben. Das wären also die verschiedenen Arten und Weisen, wie Klassen miteinander in Beziehung treten können. Auf der einen Seite haben wir diese beiden Hierarchien, die Klassenhierarchie und die Objekthierarchie, die schon ein objektorientiertes System komplett darstellen können, zumindest in allen Klassen, die man für so ein System braucht. Dann haben wir noch das Assoziationsnetz, das also sehr dicht und vielfältig gewoben sein kann zwischen den verschiedenen Klassen. Das alles macht ein objektorientiertes System aus. Jetzt möchte ich noch ganz kurz skizzieren, wie man diese verschiedenen Arten und Weisen im System in C# darstellt. Deswegen mache ich hier noch eine Konsolenapplikation dazu. Ich nenne das Ganze "Codierung" dieses Modells, und jetzt kann ich in diesem System verschiedene Klassen anlegen. Da würde ich normalerweise für jede Klasse eine eigene Datei anlegen. Aber damit wir hier nicht den Überblick verlieren, schreibe ich einfach alle Klassen in eine Datei, damit wir schnell hin und her navigieren können und das Ganze einigermaßen im Überblick haben. Fangen wir bei "Kostenpunkt" an. Und nun habe ich meine Vererbungslinien von "Kilometerpauschale" zu "Kostenpunkt". Der Vererbungspfeil oder diese Vererbungslinie wird in C# durch einen Doppelpunkt dargestellt. Genauso ist es natürlich mit der Tagespauschale, die ich mir an der Stelle spare, und stattdessen gleich übergehe zu den Kompositionen. Und jetzt gehe ich in dieser Komposition von "Kunde" nach "Bestellung". Also habe ich hier diese Class "Kunde", und ich habe die Klasse "Bestellung". Jetzt kann ich sagen: Ein Kunde hat viele Bestellungen, oder kann viele Bestellungen haben, also codiere ich so etwas als eine Liste: "List of Bestellung", so wird das gesprochen. Und ich habe mir angewöhnt, diese Liste meistens gleich zu initialisieren, damit immer eine Liste vorliegt und man die sofort benutzen kann. Das wäre jetzt eine Beziehung, die man mit einem Pfeil darstellt, weil ja nur eine Navigation von Kunde nach Bestellungen geht. Aber, ich kann hier zurück navigieren, und in dem Sinn habe ich eine Navigation in beide Richtungen. Normalerweise würde man jetzt folgendermaßen vorgehen. Bei einer Komposition würde ich sagen: Das Erstellen einer Bestellung, das ist eigentlich eine Geschichte, die immer die Kundenklasse erledigen sollte. Also könnte man das z. B. so schreiben, dass es ein gewisses Pattern, das nennt sich "Factory Method", also Fabrikmethode, erzeugt. Und mit dieser Fabrik erzeuge ich jetzt Bestellungen. Und normalerweise schreibt man hier ein "New" davor und dann den Namen der Klasse, von der ich ein Objekt erzeugen will. Jetzt kann ich eine solche Bestellung neu anlegen. Wenn ich das gleich mit irgendetwas initialisieren will, könnte ich das hier als Parameter mitgeben und könnte das hier gleich an einen entsprechenden Konstruktor weiterleiten, oder entsprechende Properties setzen. Das wollen wir uns in diesem Fall sparen. Bis auf eine kleine Sache: Da die Bestellung ja so an den Kunden geknüpft ist, kann ich jetzt hier einen Konstruktor schreiben, der ein Kundenobjekt erwartet, eben genau dieses Elternobjekt, und kann dann die interne Instanz, also diesen Rückverweis auf den entsprechenden Kunden gleich initialisieren. Dann kann ich hier einfach das Schlüsselwort "this" eintragen und übergebe das gegenwärtige Objekt. Und jetzt kann ich hier die Bestellung zurückgeben. Jetzt fehlt noch etwas, und zwar, muss ich natürlich hier die Bestellungen um diese neue Bestellung ergänzen. Das ist das übliche Pattern, mit dem man eine Komposition darstellt. Etwas anders läuft es bei der Assoziation. Ich möchte von diesem Diagramm die Assoziation zwischen Kategorie und Produkt darstellen. Ich nehme absichtlich diese Assoziation und nicht diese hier, weil die eine andere Kardinalität hat und von daher die Ähnlichkeiten nicht so groß sind und damit die Unterschiede in der Logik nicht so klar darstellbar sind, wie wenn ich Kategorie und Produkt verwenden würde. Ich habe jetzt hier diese Klasse "Produkt", und ich habe die Klasse "Kategorie", die zu einem Shop-System oder so gehören könnte. Und diese Kategorie hat verschiedene Produkte, die ihr zugeordnet sind. Und so ein Produkt kann durchaus eine Rück-Navigation zu seiner Kategorie haben. Das macht auch absolut Sinn, dass man einem Produkt ansehen kann, von welcher Kategorie es ist. Nur ist es so: In einem System lege ich die Objekte eigenständig an und verdrahte sie erst danach. Das heißt, ich habe jetzt einfach Methoden wie z. B. "add product" und kann das Produkt dieser Produktliste hinzufügen. Wenn das der Fall ist, dann müsste ich im Prinzip dem Produkt auch sagen, dass es jetzt eine neue Kategorie hat, damit man die Konsistenz mit so einer Methode sicherstellt. Im Grunde war es das eigentlich schon. Und jetzt braucht es einen übergeordneten Kontext. Das wäre jetzt eine Klasse "Shop", und dieser Shop hat eine Kategorie "K", die wird von irgendwoher initialisiert. Ich habe jetzt also ein gültiges Kategorie-Objekt, und dann hole ich mir von irgendwoher ein Produkt-Objekt, aus irgendeinem dunklen Blackbox-Applikationsteil, und nun habe ich zwei initialisierte Objekte und kann jetzt sagen: Ich brauche natürlich hier eine Methode. Jetzt habe ich den ganzen Code einfach nur in die Klasse reingeschrieben. Also nehme ich hier irgendeine Methode dieser Klasse "Shop" und kann jetzt hier mit dem Objekt "Cut" arbeiten. Und diese Methode "Add Product" oder "Add Produkt", die muss natürlich public sein, damit ich sie hier ansprechen kann. Der Compiler gibt erst Ruhe, wenn diese Objekte hier in irgendeiner Weise initialisiert sind. Und deswegen lege ich die jetzt mit Nullwerten an. Erst dann erlaubt mir der Compiler diese Zuweisung oder Übergabe des Objekts "P" an diese Methode, ohne dass er meckert. Das ist also das typische Szenario einer Assoziation: dass ich Objekte zuerst selbstständig anlege oder von irgendwoher besorge und dann miteinander verdrahte. Im Gegensatz zur Komposition, wo das Anlegen von z. B. so einer Bestellung ganz klar an das Eltern-Objekt geknüpft ist, man sollte das System so schreiben, dass man bei der Bestellung gar nicht anders anlegen kann, als im Kontext des Kunden, zu dem die Bestellung  gehört. Das waren die 3 Möglichkeiten, wie wir Relationen darstellen können. Es wäre vielleicht noch ganz interessant, auch andere Kardinalitäten zu betrachten, aber die sind relativ einfach und ähneln sich auch sehr stark. Also wenn ich jetzt hier z. B. diese Klasse "Bestellposten" hätte, und diese Klasse verweist ja auf ein Produkt. Das heißt, wenn ich so einen Bestellposten anlege, würde das in der Klasse "Bestellung" passieren, die haben wir hier, so eine Bestellung. Die hat hier wieder eine Factory-Methode. Hier könnte man jetzt z. B. ein Produkt übergeben und eine Anzahl. Jetzt könnte man hier einen Bestellposten anlegen und könnte eigentlich gleich im Konstruktur die entsprechenden Daten mit eingeben und dann sagen: Das ist eine Bestellung, und die hat eine Liste von Bestellposten und der kann man dann diesen Bestellposten zuordnen. Die Liste sollte man, wie gesagt, immer gleich initialisieren, dann kann nichts schiefgehen. Jetzt komme ich zurück zur Klasse "Bestellposten", die braucht jetzt einen Konstruktor, der ein Produkt übernimmt, und diese Anzahl. Wir machen das so, dass die Felder oben stehen. Jetzt kann man den Mitgliedsvariablen, oder Feldern dieser Klasse, die entsprechenden Werte aus dem Konstruktor zuordnen. Auf diese Weise haben wir diese 1:1 Assoziation hergestellt. Und gar nicht unähnlich entstünden 1:1 Kompositionen, wo man dann wieder dieses "New Pattern" verwendet. Hier muss man den Bestellposten natürlich noch zurückgeben, sodass man innerhalb dieser Factory-Methode das Element eben nicht einer Liste zuordnet, sondern es einer Einzelvariable zuweist. So weit, so gut. Damit habe ich die ganzen möglichen Beziehungen zwischen Klassen im Code einmal kurz skizziert, und Sie haben sie auch im Modell gesehen.

Visual C# 2012 Grundkurs

Schreiben Sie eigene Programme in C# und lernen Sie dazu alle Schlüsselwörter und die meisten Konstrukte kennen, um sicher mit dieser Programmierspreche umzugehen.

7 Std. 1 min (44 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!