Seid ihr auch schon einmal über den Begriff Objektorientierte Programmierung gestolpert? Kennt ihr die Programmierung als Abfolge von Befehlen und wollt jetzt mit Objekten einen anderen vielleicht besseren Ansatz verfolgen? Was hat das mit der Abfolge noch zu tun? Was bringen mir denn Objekte und was kann ich mit ihnen machen? Wenn ihr euch solche Fragen stellt, dann seid ihr hier genau richtig. Denn ich werde euch den Unterschied an Beispielen erklären.

Warum Objektorientiert?

Bisher wurde der eher prozedurale Ansatz gewählt. Man schreibt eine Prozedur und hüpft zur Not im Code herum. Die Objektorientierung möchte dagegen das Problem so abbilden, wie es schon bei der Planung der Anwendung oft getan wird. Man erstellt einen Ablaufplan mit detailierten Objekten und verbindet diese durch Linien. Damit will man Abhängigkeiten, Zugehörigkeiten abbilden. Umgesetzt wird es dann tatsächlich aber nicht so sauber. Mit Objekten kann man diese Dinge direkt umsetzen. Um das mal ganz kurz zu umreisen. Angenommen du möchtest eine Stadt programmieren, die mit Menschen bevölkert ist. Dann wirst du mit dem prozeduralen Ansatz das Problem lösen können. Aber ist es auch elegant? Mit Objekten löst man das Problem so... Stadt->Stadtteil->Straße->Haus->Wohnung->Personen->Person. Jeder der hier aufgeführten Begriffe sind dann die entsprechenden Objekte. Und die Pfeile zeigen die Richtung, wie du in der Stadt Köln zu Max Mustermann kommst. Und vielleicht kannst du sogar direkt Stadt->Max Mustermann aufrufen, weil das Objekt Person transparent verfügbar ist oder weil Stadt seine Eigenschaften an die anderen Objekte einfach vererbt.

Was ist ein Objekt?

Und jetzt muss ich ganz kurz unterbrechen. Ich werde jetzt 2 Wörter verwenden, das deutsche Wort Objekt und das englische Pedant Object. Wenn ich vom englischen Wort rede, dann ist tatsächlich das Object aus der Programmiersprache VB.Net gemeint.

Ein Objekt kann alles sein. Streng genommen sind in jeder Sprache, die OO unterstützt, selbst String, Integer, Boolean usw. Objekte, also die einfachsten Datentypen. Doch diese Datentypen besitzen in VB keine direkten Eigenschaften (Propertys). Trotzdem kann man sie als Object darstellen. Das Object ist die niedrigste Form der Objekte. Die Klasse Object in .Net VB enthält nur wenige Funktionen, die dann für alle erbenden Klassen gelten. Zur Vererbung später mehr.

Objekte Grundlagen

Wenn du dich so anschaust, dann hast du bestimmte Eigenschaften. Du hast wenigstens eine Größe und du hast ein Gewicht. Mindestens dies, zeichnet dich aus. Also bist du ein Objekt mit einer Größe und mit einem Gewicht. Auch dein Nachbar hat eine Größe und auch er hat ein Gewicht. Und wenn du in Hamburg wohnst, dann hast du eine Adresse in Hamburg und dein Nachbar hat auch eine ähnliche Adresse. Ein Mensch aus Bayern, der dich und deinen Nachbarn nicht kennt, würde dich im ersten Schritt als Person bezeichnen. Wenn er dich gesehen hat, dann bezeichnet er dich als Person mit Größe X und Gewicht Y. Er gibt dir Eigenschaften. So ist es auch bei der OO Programmierung. Versuchen wir das im Detail. Wir erstellen eine Klasse Person und weisen ihr 3 Eigenschaften zu. Einmal die Größe(Size), das Gewicht(Weight) und das Alter(Age).

''' <summary>
''' Öffentliche Klasse Person
''' </summary>
Public Class Person

    ''' <summary>
    ''' Eigenschaft Gewicht. Setzt das Gewicht in Gramm oder gibt es in Gramm zurück.
    ''' </summary>
    ''' <returns>Gibt das Gewicht in Gramm zurück</returns>
    Public Property Weight As Integer
    ''' <summary>
    ''' Eigenschaft Groesse. Setzt die Größe in Zentimeter oder gibt es in Zentimeter zurück.
    ''' </summary>
    ''' <returns>Gibt die Größe in Zentimeter zurück</returns>
    Public Property Size As Integer

    ''' <summary>
    ''' Das Alter der Person
    ''' </summary>
    ''' <returns></returns>
    Public Property Age As Integer

End Class

Die Klasse Person haben wir nun als Objekt definiert. Ihre Eigenschaften (Propertys) haben wir ebenfalls definiert. Von nun an können wir Person instanzieren.

Dim p As New Person

Dabei wird p zum Objekt Person. So erbt das Objekt Person automatisch alle Methoden von Object. Doch wir können auch eigene Methoden implementieren. Es wäre doch gut wenn wir das Alter durch eine Funktion einfach erhöhen können. So muss man an einem Geburtstag einfach nur noch die Funktion aufrufen und brauch sich nicht um das tatsächliche Alter weiter kümmern. Implementieren wir die Funktion IncreasingAge.

    ''' <summary>
    ''' Erhöht das Alter um 1, wenn das Alter > 0 ist
    ''' </summary>
    ''' <returns>True, wenn das Alter erfolgreich erhöht wurde, sonst false</returns>
    Public Function IncreasingAge() As Boolean
        If Age > 0 Then
            Age = +1
            Return True
        Else
            Return False
        End If
    End Function

Die Funktion haben wir so gestaltet, dass sich das Alter nur erhöht, wenn das Alter größer 0 ist. Wenn wir eine neue Instanz von Person anlegen, dann ist das Alter automatisch 0. Wir müssen also erst das Alter setzen, bevor wir es auch erhöhen können. So sollten wir die Eigenschaften eines Objektes immer entweder automatisch mit Voreinstellungen belegen oder so programmieren das es logisch nachvollziehbare Methoden besitzt. Und da wir Propertys verwenden, haben wir komfortable Möglichkeiten Einfluss auf die Werte der Eigenschaften zu nehmen. Dies ist wieder ein Vorteil der OO Programmierung. Für die Leute die mitdenken: Ja ich weiß, dass dies nicht gerade logisch ist. Ein Kind welches gerade erst geboren wurde, hat nun einmal das Alter 0. Aber es ist ein Beispiel, also nehmt diese kleine Fehlkonstruktion jetzt einfach als gegeben hin.

Property

Ein Property enthält automatisch 2 wichtige Funktionen. Das sind die sogenannten Getter und Setter Methoden. Zwar ginge ein "Public Age as Integer" auch, aber hier könnten unlogische Werte platziert werden. So kann eine Person auch schnell einmal -20 Jahre alt sein. Und das macht absolut keinen Sinn. In VB.Net sind die Getter und Setter Methoden automatisch im Hintergrund aktiv. Wir können sie auch nach vorn holen und sichtbar machen. Dazu setzen wir unseren Cursor hinter die Deklaration tippen auf "Enter" und geben ein "Get" ein, gefolgt von einem weiteren "Enter". Visual Studio erstellt nun die Struktur von selbst.

    ''' <summary>
    ''' Eigenschaft Gewicht. Setzt das Gewicht in Gramm oder gibt es in Gramm zurück.
    ''' </summary>
    ''' <returns>Gibt das Gewicht in Gramm zurück</returns>
    Public Property Weight As Integer
        Get

        End Get
        Set(value As Integer)

        End Set
    End Property

Jetzt haben wir die Getter und Setter des Property freigeschalten. Wenn wir Später "p.Weight = 100" eingeben, dann wird intern die Setter Methode "Set(value as Integer)" aufgerufen. Und hier können wir eingreifen. Genauso funktioniert das mit der Get Methode, diese wird automatisch aufgerufen wenn wir "p.Weight" abrufen. Es ist also kein "p.Weight.Get" oder "p.Weight.Set()" nötig. Doch was geben wir in der Get Methode zurück. Wenn wir die Getter und Setter unsichtbar lassen, dann erstellt der Compiler im Hintergrund automatisch eine Private Variable, die in unserem Fall "_Weight" heißen würde. Wir müssen das nun selbst erstellen. Was ich persönlich dann doch etwas unlogisch finde. So erstellt mir Visual Studio die Struktur des Property aber keine Private Variable. Das kann Eclipse mit Java deutlich besser, auch wenn man hier die Getter und Setter ähnlich wie Visual Studio nur mit einem Rechtsklick auf die Klasse erstellen kann. Also erstellen wir die Private Variable "_Weight" und ergänzen unseren Property.

    Private _Weight As Integer

    ''' <summary>
    ''' Eigenschaft Gewicht. Setzt das Gewicht in Gramm oder gibt es in Gramm zurück.
    ''' </summary>
    ''' <returns>Gibt das Gewicht in Gramm zurück</returns>
    Public Property Weight As Integer
        Get
            Return _Weight
        End Get
        Set(value As Integer)
            _Weight = value
        End Set
    End Property

Nun haben wir das Gewicht rudimentär erweitert und das Gleiche machen wir jetzt mit den anderen Propertys auch. Dann sieht unsere Klasse komplett so aus.

''' <summary>
''' Öffentliche Klasse Person
''' </summary>
Public Class Person

    Private _Weight As Integer

    ''' <summary>
    ''' Eigenschaft Gewicht. Setzt das Gewicht in Gramm oder gibt es in Gramm zurück.
    ''' </summary>
    ''' <returns>Gibt das Gewicht in Gramm zurück</returns>
    Public Property Weight As Integer
        Get
            Return _Weight
        End Get
        Set(value As Integer)
            _Weight = value
        End Set
    End Property

    Private _Size As Integer

    ''' <summary>
    ''' Eigenschaft Groesse. Setzt die Größe in Zentimeter oder gibt es in Zentimeter zurück.
    ''' </summary>
    ''' <returns>Gibt die Größe in Zentimeter zurück</returns>
    Public Property Size As Integer
        Get
            Return _Size
        End Get
        Set(value As Integer)
            _Size = value
        End Set
    End Property

    Private _Age As Integer

    ''' <summary>
    ''' Das Alter der Person
    ''' </summary>
    ''' <returns></returns>
    Public Property Age As Integer
        Get
            Return _Age
        End Get
        Set(value As Integer)
            _Age = value
        End Set
    End Property

    ''' <summary>
    ''' Erhöht das Alter um 1, wenn das Alter > 0 ist
    ''' </summary>
    ''' <returns>True, wenn das Alter erfolgreich erhöht wurde, sonst false</returns>
    Public Function IncreasingAge() As Boolean
        If Age > 0 Then
            Age = +1
            Return True
        Else
            Return False
        End If
    End Function
End Class

Jetzt können wir uns auch der Problematik des Alters widmen ohne den Alter=0 Konstruktionsfehler zu wiederholen. Wir erweitern die Set Methode des Alter Age durch einige Zeilen Code.

    ''' <summary>
    ''' Das Alter der Person
    ''' </summary>
    ''' <returns></returns>
    ''' <exception cref="System.ArgumentOutOfRangeException">Wird ausgelöst, wenn das Alter kleiner 0 und größer 150 ist.</exception>
    Public Property Age As Integer
        Get
            Return _Age
        End Get
        Set(value As Integer)
            If value >= 0 AndAlso value <= 150 Then
                _Age = value
            Else
                Throw New ArgumentOutOfRangeException("value", "Das Alter kann nur zwischen 0 und 150 liegen.")
            End If
        End Set
    End Property

Nun darf das Alter nur noch zwischen 0 und 150 liegen, ansonsten wird ein Fehler ausgelöst und die Programmausführung unterbrochen. Damit andere wissen, dass hier eine Exception ausgelöst werden kann, müssen wir das in der Beschreibung des Property verankern. Visual Studio hilft auch hier, es reicht wenn man "<exc" eingibt Visual Studio bietet sofort Codevervollständigung an. Nun beheben wir in der Funktion "IncreasingAge" die Fehlkonstruktion "Age > 0" mit "Age >=0". Und schon haben wir ein Objekt mit Person und 3 Eigenschaften fertig.

Kleine Erweiterung der Klasse Person um das Geschlecht

Zum Schluss erweitern wir noch die Klasse Person um das Geschlecht. Da es nur 2 Geschlechter gibt, wollen wir auch die Benutzung komplett einschränken. Um eine Auswahl einzuschränken, bietet sich ein Enum geradezu an.

    ''' <summary>
    ''' Enum mit der Auswahl des Geschlechts
    ''' </summary>
    Public Enum Sexes
        Male
        Female
    End Enum

    Private _Gender As Sexes = Sexes.Female

    ''' <summary>
    ''' Setzt das Geschlecht einer Person oder gibt es zurück. Default = Weiblich.
    ''' </summary>
    ''' <returns>Das Geschlecht einer Person</returns>
    Public Property Gender As Sexes
        Get
            Return _Gender
        End Get
        Set(value As Sexes)
            _Gender = value
        End Set
    End Property

Der Enum ist eine Auflistung von Werten. Dabei nimmt in unseren Beispiel Male automatisch den Wert 0 ein und Female den Wert 1. Wenn man noch weitere Werte hinzufügt, erhöht sich dieser Wert automatisch um 1. Natürlich kann man dies durch weitere Einträge erweitern. In der Programmierung nehmen uns Enums extrem viel Arbeit ab. Wir haben auch das Property "Gender" erstellt, welches durch die Private Variable "_Gender" automatisch den Wert "weiblich" annimmt. Und es kann auch nur die beiden Werte aus dem Enum annehmen. Selbst der Versuch Gender durch den Integer 0 oder 1 zu setzen, wird durch den Compiler mit einem Fehler quittiert. Wir haben also mit dem Property Gender und dem Enum Sexes eine typensichere Konstruktion erstellt. Diese kann von außen nicht unterwandert werden und erleichtert zudem die Programmierung, da wir nicht mit Zahlen hantieren müssen.

Vererbung

Eines der wichtigsten Merkmale von OO ist die Vererbung. Natürlich beschreibt uns die Klasse Person nicht den tatsächlichen Umfang einer Person. Aber mit den 4 Eigenschaften und der einen Methode können wir trotzdem schon eine Menge anfangen. So können wir 2 Personen erstellen, sagen wir Alice und Bob. Alice ist 1,70 Meter groß, wiegt 54 Kilo, ist 29 Jahre alt und natürlich ist sie weiblich. Bob ist 1,76 Meter groß, wiegt 75 Kilo, ist 36 Jahre alt und männlich. Wir können nun 2 Möglichkeiten nutzen um beide Personen abzubilden. Die erste wäre ziemlich einfach. Wir deklarien Alice und Bob als Person und füllen alle Werte aus. Alternativ können wir aber eine weitere Methode nutzen, um zum Beispiel weitere Einschränkungen vorzunehmen. Wir erstellen 2 Klassen, die Person erben und Mann und Frau abbilden.

''' <summary>
''' Klasse für die Frau
''' </summary>
Public Class Female
    Inherits Person

End Class
''' <summary>
''' Klasse für den Mann
''' </summary>
Public Class Male
    Inherits Person

End Class

Die Wörter "inherits Person" fügt alle Eigenschaften und Methoden, der Klasse Person, in die Klasse Female bzw. Male ein. Nun haben wir weiter die Möglichkeit, die Eigenschaften und Methoden der Klasse Person zu überschreiben. Es macht durchaus Sinn, wenn wir die Eigenschaft Gender durch einen festen Wert überschreiben. Eigenschaften die wir nicht anfassen wollen, wie Alter oder Größe, werden von Male und Female transparent durchgeleitet. Und das passiert ohne weiteren Code. Male und Female erben also die komplette Struktur von Person. Wenn wir also "Dim m as new Male" aufrufen, so können wir mit "m.Age" das Alter direkt setzen. Und das obwohl nicht eine einzige Eigenschaft oder Methode in der Klasse Male existiert.

    ''' <summary>
    ''' Gibt das Geschlecht von männlich zurück
    ''' </summary>
    ''' <returns></returns>
    Public Shadows ReadOnly Property Gender As Sexes
        Get
            Return Sexes.Male
        End Get
    End Property

Da wir das Property "Gender" in der Klasse "Person" als nicht überschreibbar deklariert haben, müssen wir in VB.Net das "Quasi" Überschreiben als Shadows angeben. Dies zeigt dem Compiler das wir diese Deklaration erneut vornehmen. Wir könnten versuchen die ursprüngliche Eigenschaft auch als Überschreibar zu deklarieren (Overridable), aber wir werden feststellen, dass auch dies der Compiler nicht möchte, da wir ein ReadOnly implementiert haben und das Gender in der Klasse Person als schreib- und lesbar deklariert ist. Wir müssen in diesem Fall also zwingend Shadows statt Overrides verwenden. Trotzdem wird Bob nun als männlich angezeigt, weil wir eine ReadOnly Eigenschaft nicht mehr ändern können.

   Dim Bob As New Male
   MessageBox.Show([Enum].GetName(GetType(Person.Sexes), Bob.Gender))

Versuchen wir hier Bob.Gender zu setzen, dann werden wir definitiv einen Fehler erhalten. Da Gender in Male als nur lesbar deklariert wurde.

Nun setzen wir den Rest von Bobs Eigenschaften.

        Dim Bob As New Male
        Bob.Age = 36
        Bob.Size = 176
        Bob.Weight = 75000

Das Gewicht hatten wir in Gramm angeben, die Größe in Zentimeter und das Alter in Jahren. Nun haben wir 2 Objekte erzeugt und 1 Objekt in ein anderes vererbt. Ich denke, dass man mit dieser Methode starke Klassen und weniger Code schreiben wird. Natürlich hat auch OO seine Schwachstellen. So wird eine hier eher undurchdachte Lösung, schnell zu unübersichtlichen Code führen. Also dass was man mit OO eigentlich vermeiden wollte. Wer sich seine Struktur aber vorher genau überlegt und Wert darauf legt, dass doppelter Code verschwendete Ressourcen sind, dem hilft der Aufbau einer definierten OO Struktur ungemein. So kann man sehr viel selbst geschriebenen Code ohne große Änderungen z.B. schnell in andere Projekte übernehmen. Beim nächsten mal werden wir das Ganze noch ein wenig vertiefen und schaffen eine kleine Familie, die wir später vielleicht in ein Haus einziehen lassen.

So weit....

Weiter zu Teil 2