Zum Hauptinhalt springen
Version: 1.4

Objektorientierte Modellierung & Programmierung (OOPM)

IN-SA TB #001–013


Einführung

Seit Beginn der 90er Jahre hat sich die OOP (Objektorientierte Programmierung) in der Praxis mehr und mehr durchgesetzt.

Vorteile

  • Wiederverwendbarkeit von schon programmierten Einheiten
  • Aufteilung in überschaubare Einzelteile
  • Erweiterung durch Schnittstellen

Das objektorientierte Paradigma teilt sich in zwei miteinander verzahnte Bereiche auf:

BereichBedeutung
OOMObjektorientierte Modellierung
OOPObjektorientierte Programmierung

Alan Kay: „Alles ist ein Objekt"

Die Grundidee der OOP und OOM ist, Daten und sogenannte Methoden, die auf diese Daten arbeiten, in einem sogenannten Objekt zusammenzufassen.


Wichtige Begriffe

BegriffDefinition
KlasseBauplan für Objekte. Darin werden die grundlegenden Gemeinsamkeiten der einzelnen Objekte festgelegt.
Objekt (auch Instanz)Ein konkretes Exemplar einer Klasse mit eindeutigem Objektnamen und ganz bestimmten festgelegten Eigenschaften (Attributwerten).
AttributEigenschaften eines Objekts.
MethodeFähigkeiten eines Objektes (Algorithmen).

UML – Unified Modelling Language

„Sprache" unter anderem zur Veranschaulichung von Klassen und Beziehungen zwischen ihren Objekten.

Aufbau einer Klassenkarte (UML-Klassendiagramm)

┌─────────────────────┐
│ Klassenname │ ← Name der Klasse
├─────────────────────┤
│ - attribut1 : Typ │ ← Attribute (privat = -)
│ - attribut2 : Typ │
├─────────────────────┤
│ + methode1() │ ← Methoden (öffentlich = +)
│ + methode2() │
└─────────────────────┘

Zugriffsmodifikatoren (Sichtbarkeit)

SymbolSichtbarkeitBedeutung
-privateNur innerhalb der Klasse sichtbar
+publicVon überall sichtbar
#protectedFür Unterklassen sichtbar

Das Geheimnisprinzip (Datenkapselung)

Nur Methoden sollen Attributwerte ändern!

  • Alle Attribute sind privat (-)
  • Methoden sind in der Regel öffentlich / public (+)

Beispiel: Klasse Auto

UML-Klassendiagramm

┌────────────────────────┐
│ Auto │
├────────────────────────┤
│ - geschwindigkeit: int │
│ - farbe: string │
│ - tankfüllung: int │
├────────────────────────┤
│ + gasgeben() │
│ + bremsen() │
│ + tanken(menge: int) │
└────────────────────────┘

Übersetzung in Python

class Auto:

# Konstruktor
def __init__(self, farbe):
self.__geschwindigkeit = 0
self.__farbe = farbe
self.__tankfüllung = 20

def gasgeben(self):
# Algorithmus
if self.__tankfüllung > 0:
self.__geschwindigkeit += 1
self.__tankfüllung -= 1
else:
print("Tanken!")

def bremsen(self):
if self.__geschwindigkeit > 0:
self.__geschwindigkeit -= 1
else:
print("Fahrzeug steht!")

def tanken(self, menge):
self.__tankfüllung += menge


# Objekte erzeugen
auto1 = Auto("rot")
auto1.tanken(40)

for i in range(65):
auto1.gasgeben()

Der Klassenkonstruktor

Zur Erzeugung eines Objektes wird der sogenannte Konstruktor aufgerufen – eine spezielle Methode, mit der Attribute initialisiert werden (d.h. die Startwerte können festgelegt werden).

Man sagt: Ein Objekt wird durch den Aufruf des Konstruktors erzeugt und initialisiert.

Konstruktor in Python

def __init__(self, parameterliste):
self.__attributname = wert # Attribute

Aufruf des Konstruktors

objektbezeichner = Klassenname(Parameterwerte)

Beispiel: Klasse Sim

class Sim:
def __init__(self, name):
self.__alter = 17
self.__name = name

sim1 = Sim("Bob")

Punktnotation

Nachdem ein Objekt erzeugt wurde, können seine Attribute durch das Aufrufen seiner Methoden geändert werden. Auf die Methoden eines Objekts kann durch Verwendung der Punktnotation zugegriffen werden:

objektname.methodenname()
sim1.trinken()

Standardmethoden: Getter und Setter

Wegen des Geheimnisprinzips können und sollen Attribute nur durch Methoden verändert werden. Innerhalb der Methoden können Bedingungen formuliert werden, die die Daten im Sinne des Programmierers schützen.

def getAlter(self):
return self.__alter # gibt den Wert von Alter zurück

def setAlter(self, alter):
self.__alter = alter # setzt den Wert von Alter

def getName(self):
return self.__name

def setName(self, name):
self.__name = name

Klassendiagramm & Assoziationen

Klassen können in Beziehungen zueinander gestellt werden. Beziehungen zwischen Objekten von Klassen nennt man Assoziationen.


Aggregation

Um ein Objekt in Beziehung zu seinen Bestandteilen zu sehen, verwendet man Assoziationen – sog. Aggregation.

In UML: Klasse1 ◇──── Klasse2

Das heißt: Ein Objekt der Klasse1 enthält/besitzt ein Objekt der Klasse2.

Beispiel: Sim besitzt ein Bett

class Sim:
def __init__(self):
self.__bett = None

def bettkaufen(self, bett):
self.__bett = bett

def schlafen(self):
self.__bett.setGemacht(False)

Bekanntschaft zwischen Klassen

Eine Beziehung zwischen zwei Klassen liegt vor, wenn:

  • Klasse1 mindestens eine Methode mit einem Übergabeparameter vom Typ Klasse2 definiert, oder
  • Klasse2 mindestens ein Attribut vom Typ Klasse1 besitzt.

Methoden mit Rückgabe (Getter)

Methoden mit Rückgabe besitzen als letzte Anweisung im Methodenrumpf ein return-Statement.

In UML

methode1(u: int) : int

In Python

def methode1(self, u):
# Algorithmus
return ... # int

Ruft man eine Methode auf, die eine Rückgabe generiert, kann man diesen Wert an der Stelle des Aufrufs erwarten:

antwort = objekt.methoden(1, 2)
# Methode wird aufgeführt, Rückgabe wird generiert

Lokale vs. Globale Variablen

TypGültigkeitBeispiel
Lokale VariableNur innerhalb der Methode, in der sie definiert wirdy = 5 in einer Methode
Globale VariableFür alle Methoden (= Attribute)x = 5 auf Klassenebene
x = 5  # global

def methode(self):
y = 5 # lokal

Datentypen

Primitive Datentypen

TypBeschreibung
intGanzzahl
stringZeichenkette
floatGleitkommazahl
boolWahrheitswert (True / False)

Zusammengesetzte Datentypen (Klassen)

Ein Attribut kann selbst den Typ einer Klasse haben:

Mensch:
- gebDat: Datum

Datum:
- tag: int
- monat: int
- jahr: int

Vererbung

Konzept

Eine Vererbungsbeziehung (auch: Generalisierung / Spezialisierung) liegt vor, wenn eine Klasse alle Attribute und Methoden einer anderen Klasse erbt. Die erbende Klasse heißt Unterklasse, die vererbende Klasse heißt Oberklasse (auch: Superklasse).

Ziel: Vermeidung von doppeltem Code.

Die Unterklassen erben dabei alle Attribute und Methoden der Oberklasse. Methoden können überschrieben werden, falls sie unpassend sind – das nennt man Polymorphie.

Sichtbarkeit bei Vererbung

Attribute, die für Unterklassen zugänglich sein sollen, müssen protected (#) statt private (-) sein.

In UML

        Charakter
/ | \
Elf Troll Zwerg

Offener Pfeil von Unterklasse zur Oberklasse.

Beispiel-Klassendiagramm

┌──────────────────────┐
│ Charakter │
├──────────────────────┤
│ * name: str │
│ # lebenspunkte: int │
├──────────────────────┤
│ + angreifen() │
└──────────┬───────────┘

┌──────┴──────┬──────────┐
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌────────┐
│ Elf │ │ Troll │ │ Zwerg │
├────────┤ ├─────────┤ ├────────┤
│-gold: │ │-steine: │ │-erz: │
│ int │ │ int │ │ int │
├────────┤ ├─────────┤ ├────────┤
│+singen │ │+angreif.│ │+erzabb.│
└────────┘ └─────────┘ └────────┘

Vererbung in Python

class Charakter:
def __init__(self, name, lebenspunkte):
self._name = name # protected (1 Unterstrich)
self._lebenspunkte = lebenspunkte

def angreifen(self):
print("wusch")


class Elf(Charakter): # Elf erbt von Charakter
def __init__(self, name, lebenspunkte, gold):
super().__init__(name, lebenspunkte) # Konstruktor der Oberklasse aufrufen!
self.__gold = gold

def singen(self):
print("lalala")


# Objekte erzeugen
frie = Elf("Frie", 100000, 1700)
frie.singen() # lalala
frie.angreifen() # wusch (geerbt!)

Wichtig: Im Konstruktor der Unterklasse muss als erste Anweisung immer super().__init__(parameterliste) stehen, um den Konstruktor der Oberklasse aufzurufen!


Vollständige UML-Notation (Übersicht)

┌──────────────────────────────────────┐
│ Klassenname │
├──────────────────────────────────────┤
│ Zugriffsmod Attributname : Datentyp │
├──────────────────────────────────────┤
│ Zugriffsmod Methodenname( │
│ Eingabeparam : Typ) │
│ : Rückgabetyp│
└──────────────────────────────────────┘
ZugriffsmodifikatorSymbol
public+
private-
protected#