12.5.4 OOP - Aggregation


Attribute in Objekten können selbst Objekte sein. Im folgenden Beispiel sollen in die Zeichenfläche Häuser gezeichnet werden. Das Ziel dabei ist, dass die realen Objekte, aus welchen ein Haus aufgebaut ist, im Programm mit Software-Objekten abgebildet werden.

Ein Haus soll aus dem Gebäude, einem Dach, einem Fenster und einer Tür bestehen. Damit während des Programmablauf neue Objekte angelegt werden können, legen wir Objektschablonen (Klassen) an. Wir legen 5 Klassen als Objekt-Schablonen fest:

  • die Haus-Klasse
  • die Gebäude-Klasse
  • die Dach-Klasse
  • die Fenster-Klasse
  • die Tür-Klasse

Zusätzlich soll das Haus in der Zeichenfläche eine bestimmte Position haben, von der aus es gebaut wird. Dazu legen wir noch eine Koordinaten-Klasse für die Position des Hauses fest.

Im Programm-Ablauf wird ein Haus aus den anderen Objekten zusammengestellt. Ein z.B. Dach-Objekt ist dann ein Bestandteil eines Haus-Objekts. Fachsprachlich sagt man: zwischen dem Dach-Objekt und dem Haus-Objekt besteht eine Aggregation. Das Symbol für eine Aggregation ist eine Linie mit einer Raute als Symbol am Ende der Linie.

Damit können die benötigten Klassen implementiert werden.

1) Implementierung der "hausKlasse". Im Konstruktor der Haus-Klasse werden im Programmablauf die anderen Objekte übergeben, so dass zwischen den Bestandteilen des Hauses und dem Haus eine Aggregation besteht.

class hausKlasse {
  constructor(koordinaten, gebaeude, dach, tuer, fenster) {
    this.koordinaten = koordinaten;
    this.gebaeude = gebaeude;
    this.dach = dach;
    this.tuer = tuer;
    this.fenster = fenster;
  }
}

2) Implementierung der "koordKlasse". In der Koordinaten-Klasse wird die x- und y-Position gespeichert, von der aus das Haus gezeichnet werden soll.

class koordKlasse {
  constructor(xkoord, ykoord) {
    this.xkoord = xkoord;
    this.ykoord = ykoord;
  }
}

3) Implementierung der "gebaeudeKlasse". Die Gebäude-Klasse ist einfach ein Rechteck.

class gebaeudeKlasse {
  constructor(farbe, breite, hoehe) {
    this.farbe = farbe;
    this.breite = breite;
    this.hoehe = hoehe;
  }
  zeichnen(xkoord, ykoord) {
    strokeWeight(0);
    fill(this.farbe);
    rect(xkoord, ykoord, this.breite, this.hoehe); 
  }
}

4) Implementierung der "dachKlasse". Das Dach hat eine Dreicksform und soll symmetrisch aufgebaut sein. Im Argument von triangle() werden die Punkte berechnet und eingetragen, damit das Dach symmetrisch wird (Mathe lässt grüßen).

class dachKlasse {
  constructor(farbe, breite, hoehe) {
    this.farbe = farbe;
    this.breite = breite;
    this.hoehe = hoehe;
  }
  zeichnen(xkoord, ykoord) {
    strokeWeight(0);
    fill(this.farbe);
    triangle(xkoord, ykoord, xkoord+this.breite/2, ykoord-this.hoehe, xkoord+this.breite, ykoord); 
  }
}

5) Implementierung der "tuerKlasse". Die Tür soll einen Türgriff haben, der als Linie in der Operation zeichnen() gezeichnet wird.

class tuerKlasse {
  constructor(farbe, breite, hoehe) {
    this.farbe = farbe;
    this.breite = breite;
    this.hoehe = hoehe;
  }
  zeichnen(xkoord, ykoord) {
    strokeWeight(0);
    fill(this.farbe);
    rect(xkoord, ykoord, this.breite, this.hoehe); 
    strokeWeight(1);
    line(xkoord+this.breite-10, ykoord+this.hoehe/2, xkoord+this.breite-5, ykoord+this.hoehe/2);
  }
}

6) Implementierung der "fensterKlasse". Das Fenster ist ein einfaches Rechteck mit einem schwarzen Fensterrahmen.

class fensterKlasse {
  constructor(farbe, breite, hoehe) {
    this.farbe = farbe;
    this.breite = breite;
    this.hoehe = hoehe;
  }
  zeichnen(xkoord, ykoord) {
    strokeWeight(1);
    stroke(0);    
    fill(this.farbe);
    rect(xkoord, ykoord, this.breite, this.hoehe); 
  }
}

7) Implementierung der Operation 'zeichnen()'. In der Haus-Klasse wird die Operation zeichnen() implementiert und darin die zeichnen-Operationen der anderen Objekte aufgerufen. Die Herausforderung besthet nun darin, die anderen Objekte so zu zeichnen, dass sie zum Gebäude passen. Dazu werden die Attribute der einzelnen Objekte genommen und abhängig davon die anderen Objekte gezeichnet. Hier hilft wieder die Mathematik, das hinzubekommen.

In der Operation zeichnen() gibt es bei jedem Objekt die Abfrage, ob es definiert ist. Beispielsweise prüft die Abfrage if (this.gebaeude != null) { ob das Attribut 'gebaeude' ungleich 'null' ist, also ob es angelegt wurde. Wenn es angelegt wurde, wird das Gebäude gezeichnet, sonst nicht. Das wird auch mit den anderen Objekten so gemacht.

class hausKlasse {
  constructor(koordinaten, gebaeude, dach, tuer, fenster) {
    this.koordinaten = koordinaten;
    this.gebaeude = gebaeude;
    this.dach = dach;
    this.tuer = tuer;
    this.fenster = fenster;
  }
  zeichnen() {
    if (this.gebaeude != null) {
      this.gebaeude.zeichnen(this.koordinaten.xkoord, this.koordinaten.ykoord);
      if (this.dach != null) {
        let dachXPos = this.koordinaten.xkoord - (this.dach.breite-this.gebaeude.breite)/2;
        this.dach.zeichnen(dachXPos, this.koordinaten.ykoord);
      }
      if (this.tuer != null) {
        let tuerYPos = this.koordinaten.ykoord + this.gebaeude.hoehe - this.tuer.hoehe;
        let tuerXPos = this.koordinaten.xkoord + this.gebaeude.breite - (this.tuer.breite + 15);
        this.tuer.zeichnen(tuerXPos, tuerYPos);
      }
      if (this.fenster != null) {
        let fensterXPos = this.koordinaten.xkoord + 15;
        let fensterYPos = this.koordinaten.ykoord + 10;
        this.fenster.zeichnen(fensterXPos, fensterYPos);
      }
    }
  }  
}

8) Anlegen globaler Variablen für die Häuser. Andere globale Variablen brauchen wir nicht, da alle anderen Attribute und Objekte in den Objekten gekapselt sind.

let haus1;
let haus2;
let haus3;
let haus4;

9) Gestaltung der Häuser. Die Häuser werden gestaltet, indem die Objekte, aus denen ein Haus besteht, festgelegt werden. Für die einzelnen Objekte wird nur die Farbe, Länge, Breite,... festgelegt, die Position wird relativ zu den Koordinaten des Hauses in der Operation zeichnen() berechnet. Es wird nur einmal eine lokale Variable für die Objekte eines Hauses angelegt, die dann im Hausobjekt gespeichert werden. Bei den anderen Häusern wird die gleiche lokale Variable verwendet und mit neuen Werten überschrieben, wodurch man Speicherplatz sparen kann.

Wenn z.B. keine Tür und/oder kein Fenster im Haus vorhanden sein soll, dann kann man null übergeben und damit wird kein Objekt angelegt und gezeichnet.

function setup() {
  createCanvas(400, 400);

  //haus1
  let koord = new koordKlasse(60, 120);
  let gebaeude = new gebaeudeKlasse("grey", 110, 50);
  let dach = new dachKlasse("darkred", 130, 40);
  let tuer = new tuerKlasse("wheat", 30, 40);
  let fenster = new fensterKlasse("burlywood", 25, 25);
  haus1 = new hausKlasse(koord, gebaeude, dach, tuer, fenster);

  //haus2
  koord = new koordKlasse(220, 140);
  gebaeude = new gebaeudeKlasse("lightgrey", 130, 50);
  dach = new dachKlasse("crimson", 160, 40);
  tuer = new tuerKlasse("green", 30, 40);
  fenster = new fensterKlasse("orange", 45, 25);
  haus2 = new hausKlasse(koord, gebaeude, dach, tuer, fenster);  

  //haus3
  koord = new koordKlasse(70, 260);
  gebaeude = new gebaeudeKlasse("teal", 80, 70);
  dach = new dachKlasse("steelblue", 120, 40);
  tuer = new tuerKlasse("lightgrey", 30, 50);
  fenster = new fensterKlasse("white", 10, 35);
  haus3 = new hausKlasse(koord, gebaeude, dach, tuer, fenster); 

  //haus4
  koord = new koordKlasse(230, 280);
  gebaeude = new gebaeudeKlasse("teal", 100, 80);
  dach = new dachKlasse("steelblue", 120, 40);
  haus4 = new hausKlasse(koord, gebaeude, dach, null, null);   
}

10) Häuser zeichnen. Schließlich werden die Häuser gezeichnet, indem man die Operation zeichnen() in jedem Hausobjekt aufruft. In der zeichnen()-Operation eines Hauses wird dann die jeweilige zeichnen()-Operation der einzelnen Objekte aufgerufen.

function draw() {
  background(240);

  haus1.zeichnen();
  haus2.zeichnen();
  haus3.zeichnen();
  haus4.zeichnen();
}

Auf dem Bildschirm sieht man dann folgende Häuser:

Das ganze Programm ist ziemlich lang und sieht auch etwas kompliziert aus, aber es können leicht neue Häuser hinzugefügt werden oder es kann leicht das Aussehen von Häusern verändert werden, indem man einfach die Parameter beim Anlegen der Objekte ändert. Der Rest wird von den Objekten gemacht.

In einem neuen Fenster starten: Häuser

Erweitern Sie das Haus mit einem weiteren Objekt ihrer Wahl (Schornstein, Dachfenster, Mülltonne, Baum,...).

In einem neuen Fenster starten: Häuser