Aggregation

Als Aggregation wird das Zusammenfassen mehrerer Objekte zu neuen bezeichnet. Wir haben bereits ein Beispiel dafür gesehen: Die Bruch-Klasse fasst einen Zähler und einen Nenner zu einem neuen Wert zusammen. Die Bestandteile sind ihrerseits jeweils als Zahl-Objekte representiert. In ähnlicher Weise könnten wir mehrere Zahlen zu Punkten in einem Koordinatensystem zusammen fassen, mehrere Punkte zu geometrischen Figuren und mehrere geometrische Figuren zu Bildern. Auf diese Weise entstehen immer komplexere, hierarchische Daten auf Basis von einfacheren.

Wir wollen im folgenden den bereits verwendeten Mechanismus der Aggregation vertiefen und dann darauf aufbauend das neue Konzept der Vererbung kennenlernen.

Dazu definieren wir eine Klasse Point zur Darstellung von Punkten in einem Koordinatensystem.

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

Die Methode __init__ konstruiert einen Punkt aus einer x- und einer y-Koordinate, indem entsprechende Parameter x und y in Instanzvariablen _x und _y (auch Attributvariablen genannt) abgespeichert werden. Durch die Benennung mit einem Unterstrich signalisieren wir, dass außerhalb der Klassendefinition kein Zugriff auf die Instanzvariablen erfolgen soll.

Um von außen lesenden Zugriff auf die Koordinaten zu erlauben, definieren wir innerhalb der Klasse Point Methoden x und y, die die Werte der entsprechenden Koordinaten zurück liefern.

    def x(self):
        return self._x
    
    def y(self):
        return self._y

Um auch schreibenden Zugriff zu erlauben, definieren wir (mutierende!) Methoden set_x und set_y, die die Koordinaten auf übergebene Werte setzen.1

    def set_x(self, x):
        self._x = x
    
    def set_y(self, y):
        self._y = y

Dass der Zugriff auf Attribute, die in Klassen für Objekte definiert sind, nur über bereitgestellte Methoden erfolgt, ist ein Ausdruck von Datenkapselung. Ein Vorteil der Datenkapselung ist es, dass wir den Zugriff einschränken können. Zum Beispiel können wir testen, ob die übergebene Koordinate eine ganze Zahl ist, und nur in disem Fall den aktuellen Wert überschreiben. Dazu verändern wir die Methoden zum Setzen der Koordinaten wie folgt.

    def set_x(self, x):
        if type(x) == int:
            self._x = x
    
    def set_y(self, y):
        if type(y) == int:
            self._y = y

Eine solche Überprüfung erscheint auch bei der Konstruktion von Punkten sinnvoll. Dazu können wir die gerade definierten Methoden im Konstruktor verwenden, dessen Implementierung wir also wie folgt verändern.

    def __init__(self, x, y):
        self.set_x(x)
        self.set_y(y)

Als nächstes definieren wir eine Klasse Shape zur Darstellung geometrischer Figuren, die als Zustand einen Punkt kapselt, der angibt, wo die Figur gezeichnet werden soll.

class Shape:
    def __init__(self, point):
        self.set_location(point)
    
    def location(self):
        return self._location
    
    def set_location(self, point):
        if type(point) == Point:
            self._location = point

Auch hier definieren wir Methoden zum lesenden und schreibenden Zugriff auf den internen Zustand der Shape-Objekte. Wir werden später noch weitere Methoden für Shape-Objekte definieren. Zunächst jedoch definieren wir spezielle geometrische Figuren als sogenannte Unterklassen der Klasse Shape.


  1. Diese Art Zugriffsmethoden zu definieren ist in Python unüblich. Eine Diskussion von Property-Decorators würde uns aber vom Wesentlichen ablenken. ↩︎