unterschiedliche Leuchtmittel an einem Wagenrad

Attribute absichern



Quellen: M. Brenner, J. Schnaiter, "Softwareentwicklung mit Python", ZSL Freiburg, 2024

Um Attributwerte (Objekteigenschaften) gegen Fehler abzusichern, überprüft man diese in Methoden. Diese Methoden heißen Setter zum Ändern von Attributwerten und Getter zum Abrufen von Daten. Um nun sicherzustellen, dass die Daten immer über diese Methoden geändert und abgerufen werden kappselt man die Attribute, d.h. man macht die Attribute privat. Dadurch kann nur noch in der Klasse auf die Attribute direkt zugegriffen werden, von außerhalb nur über die Setter und Getter.

In diesem Artikel lernst Du wie man Attributwerte mit set- und get-Methoden absichert, die Bedeutung der Datenkapselung mit public und private sowie das UML-Klassendiagramm kennen.

1 Attribute absichern - Kapselung durch set-get-Methoden

Zeichnung zweier unterschiedlicher Schafe
In der Klasse Leuchtmittel werden folgende Eigenschaften für die Leuchtmittel definiert: Leistungsaufnahme P in W (Watt), Lichtstrom Φ in lm (Lumen). Berechnen kann man hieraus die Lichtausbeute η in lm/W (Lumen pro Watt).

class Leuchtmittel:
    def __init__(self, leistung = 2.5, lumen = 340):
        self.leistung = leistung
        self.lumen = lumen

    def __str__(self):
        return self.__class__.__name__ + f" {self.leistung} W, 
		🔆 {self.lumen} lm und η = {self.lumen/self.leistung} lm/W"

Probleme bei der Objekterzeugung in der Beleuchtung Lampe

Eine Wagenradlampe der Klasse Beleuchtung besteht aus Objekten der Leuchtmittel. Wir erstellen nun ein Objekt:

class Beleuchtung:
    l1 = Leuchtmittel(2.5, 340)
    l2 = Leuchtmittel(3, 420)

    print(l1)
    print(l2) 

    #Die Leistung wird mit einem fehlerhaften Wert manipuliert.
    l2.leistung = -42.0
    print(l2)

Folgende Ausgabe wird erzeugt:

Leuchtmittel 2.5 W, 🔆 340 lm und η = 136.0 lm/W
Leuchtmittel 3 W, 🔆 420 lm und η = 140.0 lm/W
Leuchtmittel -42.0 W, 🔆 420 lm und η = -10.0 lm/W

Der Benutzer kann fehlerhafte Werte setzen wodurch nicht nur der Wert, sondern auch nachfolgende Berechnungen fehlerhaft sind.

Lösung mit Kapselung und set-get-Methoden in der Klasse Leuchtmittel

Wir ersetzen den sogenannten Modifier der Attribute in der Klasse Leuchtmittel durch private. Dies wird durch einen doppelten Unterstrich in Python gemacht __leistung. Dadurch wird der Attributzugriff auf die Klasse Leuchtmittel beschränkt. Diesen Vorgang nennt man Kapselung. Man schirmt die Attribute vom Zugriff aus anderen Klassen ab.

Die Änderung von gekapselten Werten erlaubt man dann nur noch in den sogenannten set-Methoden; für den Wertzugriff verwendet man get-Methoden. Der indirekte Zugriff auf die Leuchtmittelattribute erfolgt aus der Klasse Lampe:

class Beleuchtung:
    l1 = Leuchtmittel(2.5, 340)
    print(l1) 

    #Die Leistung wird mit fehlerhaft manipuliert.
    l1.setLeistung(-2.5)
    print(l1)

class Leuchtmittel:        
    def __init__(self, leistung = 2.5, lumen = 340):
        self.setLeistung(leistung)
        self.lumen = lumen
        
    #setter und getter für Leistung
    def setLeistung(self, leistung):
        if (leistung >= 0):
            self.__leistung = leistung #geschütztes Attribut
        else:
            print("Wert fehlerhaft, der Wert bleibt unverändert.")
    
    def getLeistung(self):
        return self.__leistung
    
    def __str__(self):
        return self.__class__.__name__ + f" {self.__leistung} W, 
		🔆 {self.lumen} lm und η = {self.lumen/self.__leistung} lm/W"
    

Modifier Sichtbarkeit von Variablen und Methoden in Python

Modifier Die Klasse selbst Unter­klassen sons­tige Klassen
private (zwei Unterstriche)
protected (ein Unterstrich)
public (kein Unterstrich)

Aufgabe 1 Die Retrobeleuchtung

Erstelle die Klasse Leuchtmittel und Beleuchtung.

  1. Erstelle die Klasse Leuchtmittel und Beleuchtung. Programmiere alle Methoden wie im Eingangsbeispiel. Vergiss dabei nicht in der to-String-Methode auch die Lichtausbeute auszugeben.
  2. In das Beispielprogramm haben sich trotz Abischerung drei Fehler eingeschlichen. Teste folgende drei Manipulationen und notiere die Fehlerart:
    l2 = Leuchtmittel(-2, 340),
    l2 = Leuchtmittel(0, 340),
    l2 = Leuchtmittel("0", 340).
  3. Sicher das Attribut __leistung gegen diese Fehler ab.
  4. Der Nutzer möchte zusätzlich für jedes Leuchtmittel einen Dimmfaktor zwischen 0 und 100 % eingeben. Gib eine Liste der Helligkeitswerte (in %) und Leistungsbedarf aus.
Retrolampe mit mehreren Leuchtmitteln

2 Mit Properties lesbare gekapselte Attribute - Verbesser die Lesbarkeit

Mit Setter und Getter wird der Zugriff auf Variablen mitunter unlesbar. Betrachten wir hierzu folgendes Beispiel. In der Klasse Beleuchtung wird die Leistung von l3 mit den Werten von l1 und l2 berechnet.

class Beleuchtung:
    l1 = Leuchtmittel(2.5,340)
    l2 = Leuchtmittel(1.5,200)
    l3 = Leuchtmittel()

    #Die Leistung von l3 wird mit den Werten von l1 und l2 berechnet.
    l3.setLeistung(l1.getLeistung()+l2.getLeistung())
    print(l3) 

Properties erstellen

Durch Properties wird der Code wieder lesbarer. Wir können die Variablen verwenden als ob sie öffentlich wären, sind sie aber nicht.

class Leuchtmittel:        
    def __init__(self, leistung = 2.5, lumen = 340):
        ...
    def setLeistung(self, leistung):
    	...
    def getLeistung(self):
        ...
    def __str__(self):
        ...

    #property
    leistung = property(getLeistung, setLeistung)

class Beleuchtung:
    l1 = Leuchtmittel(2.5,340)
    l2 = Leuchtmittel(1.5,200)
    l3 = Leuchtmittel()

    #Die Leistung von l3 wird mit den Werten von l1 und l2 berechnet.
    l3.leistung = l1.leistung + l2.leistung
    print(l3) 

Aufgabe 2 Ordne zu

Ordne die korrekte Antwort zu.

Nenne die Klasse, welche auf ein private Attribut Zugriff hat.
Wähle eine Antwort.

  1. innere Klassen
  2. die Klasse selbst
  3. Unter­klassen
  4. sons­tige Klassen

Nenne Vorteile der Kapselung.
Wähle zwei Antworten.

  1. fehlerhafte Werte werden verhindert
  2. Objekte werden nicht doppelt erzeugt
  3. Klassen werden geschützt
  4. abhängige Attribute sind geschützt

Finde die Anzahl der Fehler im folgenden Quelltext.

def setLeistung(self, __leistung):
    if (leistung >= 0):
        self.leistung = leistung
    else:
        print("Wert fehlerhaft")

Finde die Anzahl der Fehler im folgenden Quelltext.

def __init__(self, leistung):
	self.leistung = leistung

def getLeistung(self, leistung):
    return self.__leistung

Bestimme die zugehörige Konstruktormmethode, zum Konstruktoraufruf
s2 = Schaf(alter = 20, gewicht = 23.3). Wähle eine Antwort.

  1. def __init__(self, gewicht, alter)
  2. def __init__(gewicht=1.0, alter=1)
  3. def __init__(self, alter, gewicht, c="C")
  4. def __init__(self, 20, 3)

Beschreibe einen Konstruktor.
Wähle beliebig viele Antworten.

  1. ist eine Methode
  2. gibt einen Wert zurück
  3. wird durch "__init__" gekennzeichnet
  4. kennzeichnet Klassen

3 Exception - Optimierung der Absicherung

Mit Hilfe von Exceptions lassen sich verschiedene Fehlermeldungen abfangen.

class Beleuchtung:
    l1 = Leuchtmittel(2.5,340)
    l2 = Leuchtmittel(1.5,200)
    l3 = Leuchtmittel()

    #Die Leistung von l3 wird mit den Werten von l1 und l2 berechnet.
    l3.setLeistung(l1.getLeistung()+l2.getLeistung())
    print(l3) 

Properties erstellen

Durch Properties wird der Code wieder lesbarer. Wir können die Variablen verwenden als ob sie öffentlich wären, sind sie aber nicht.

class Leuchtmittel:        
    def __init__(self, leistung = 2.5, lumen = 340):
        ...
    def setLeistung(self, leistung):
    	...
    def getLeistung(self):
        ...
    def __str__(self):
        ...

    #property
    leistung = property(getLeistung, setLeistung)

class Beleuchtung:
    l1 = Leuchtmittel(2.5,340)
    l2 = Leuchtmittel(1.5,200)
    l3 = Leuchtmittel()

    #Die Leistung von l3 wird mit den Werten von l1 und l2 berechnet.
    l3.leistung = l1.leistung + l2.leistung
    print(l3) 

Aufgabe 3 set-get-Methoden und Konstruktoren für die Schafe

Die Schafe sollen mit set-get-Fähigkeiten und Konstruktoren ausgestattet werden.

  1. Erweitere die Klasse Schaf mit folgenden set-get-Methoden:
    public void setName(String name) {..}
    public void setAlter(int alter) {..}
    public void setGewicht(double gewicht) {..},
    public void setGewicht(int mengeGras, int mengeKohl ) {..},
    public String getName() {..},
    public int getAlter() {..},
    public double getGewicht() {..}.
  2. Erweitere die Klasse Schaf mit einem neuen Standardkonstuktor:
    public Schaf( ) {..}. Initialisiere den Namen mit "noname", das Alter mit 1 Jahr sowie das Gewicht mit 0.0 kg.
  3. Erweitere die Klasse Schaf mit dem weiteren Konstuktor:
    public Schaf(String name, int alter, double gewicht) {..}.
  4. Teste in der Klasse Schafherde die neuen Methoden und Konstruktoren ausführlich. Lass alle Änderungen der Attribute jeweils anzeigen.
Zeichnung zweier Schafe

Entspann dich erstmal ...



Briefmarken aus dem Jahr 2022

Ein männlicher Briefmark erlebte
Was Schönes, bevor er klebte.
Er war von einer Prinzessin beleckt.
Da war die Liebe in ihm erweckt.

Er wollte sie wieder küssen,
Da hat er verreisen müssen.
So liebte er sie vergebens.
Das ist die Tragik des Lebens!

Aufgabe 4 Der Fuhrpark

Erweitere die Klasse Auto und Fuhrpark.

zwei Autos
  1. Erweitere die Klasse Auto mit set- und get-Methoden.
  2. Setze alle Attribute der Klasse Auto auf private
  3. und stelle so sicher, dass abhängige Attribute nicht falsche Werte haben können und sicher alle Eingaben gegen fehlerhafte Eingaben ab.
  4. Erweitere die Klasse Fuhrpark durch Aufrufe der set- und get-Methoden und teste so die Funktionalität der Klasse Auto.
  5. Erweitere die Klasse Auto mit zwei Konstruktor­methoden, den Standard­konstruktor mit Initalwerten und einem Konstruktor mit Parameterliste.

UML-Klassendiagramme Unified Modeling Language

UML (engl. unified modeling language) ist eine grafische Modellierungssprache zur Spezifikation, Konstruktion und Dokumentation von Software-Teilen und anderen Systemen. UML besteht aus vielen Diagrammtypen. Ein Typ davon ist das Klassendiagramm.

Ein Klassendiagramm stellt eine Klasse übersichtlich und strukturiert dar. Es ist von der Form her rechteckig und besteht aus drei Teilen:

  1. Klassenname,
  2. Attribute (Eigenschaften) mit Modifier (+ für public und - für private) und Datentyp,
  3. Methoden (Fähigkeiten) mit Modifier, Name, Parameterliste und Rückgabetyp.

Man beachte, das statische Methoden und Attribute unterstrichen werden. Der Vorteil statischer Methoden liegt darin, dass sie ohne die Erzeugung eines Objekts aufrufbar sind: Auto.setMwSt(19.0);. Nachteilig wirkt sich aus, dass der Speicher immer belegt wird.

Was das Klassendiagramm nicht sagt:

  • es beschreibt nicht was in den Methoden genau passiert und wie diese zu ihrem Ergebnis kommen,
  • es sagt nichts über die Objekte aus, die von der Klasse erstellt werden.
Auto
- farbe : String
- leistung : int
- preis : double
- mehrwertsteuer : double
+ Auto (String farbe, int leistung, double preis)

+ setFarbe (String color) : void
+ setLeistung (int power) : void
+ setMwSt (double mwst) : void

+ getFarbe () : String
+ getLeistung () : int
+ getMwSt () : double

+ fahrenStrecke (int km) : void
+ tanken (int menge) : void

Aufgabe 5 UML-Klassendiagramm

Schreibe UML-Klassendiagramme.

  1. Schreibe ein UML Klassendiagramm für die Klasse Auto.
  2. Schreibe ein UML Klassendiagramm für die Klasse Fuhrpark.
  3. Schreibe ein UML Klassendiagramm für die Klasse Schaf.
Zeichnung zweier Autos

Aufgabe 6 Weitere Projekte

Schreibe weitere Klassenpaare.

Pizzastücke

Neben den Klassenpaaren Schaf und Schafherde sowie Auto und Fuhrpark, gibt es weitere Klassenpaare.

  1. Schreibe ein UML Klassendiagramm für die Klassen Wohnmobil und Campingplatz.
  2. Schreibe ein UML Klassendiagramm für die Klassen Pizza und Menue.
  3. Schreibe ein UML Klassendiagramm für die Klassen Sensor und Home.
  4. Nenne drei weitere Klassenpaare aus der Praxis und programmiere eins davon.

Automatisierte Objektauswertung Objekte in einer ArrayList speichern

Speichert man die verschiedenen Objekte direkt beim Erzeugen in einer ArrayList können mit Hilfe von Schleifen automatisierte Ausgaben und Auswertungen aller Objekte generiert werden. Dabei muss im Vergleich zu einem array die genaue Anzahl der Objekte vorab nicht bekannt sein. Mit der add(..) Methode wird ein Element dem Container hinzugefügt.

import java.util.ArrayList;
public class SchafherdeArrayList {
  public static void main(String[] args) {
    //Anlegen einer ArrayList namens schafListe
    ArrayList<Schaf> schafListe = new ArrayList<Schaf>();
    //Hinzufügen von Einträgen mit der Methode add(..)
    schafListe.add(new Schaf("Emma", 20, 50.0));
    schafListe.add(new Schaf("Berta", 8, 30.0));
  }
}
	
Zeichnung zweier Schafe

Konstruktoren erstellen

Um nun Standardwerte zu vergeben oder beim Erzeugen des Objekts die Attribute zu setzen, werden entsprechende Konstruktormethoden erstellt.

Des weiteren benötigen wir wie gehabt entsprechende set- und get-Methoden.

public class Schaf{
  private String name;
  private int alter;
  private double gewicht;

  //Konstruktor
  public Schaf (String n, int a, double g){
    this.name = n;
    this.alter = a;
    this.gewicht = g;
  }

  //set- und get-Methoden
  ...
}
//Ausgabe aller Schafe und deren Alter
for (int i=0; i<schafListe.size(); i++) {
  Schaf schaf = schafListe.get(i);
  System.out.println("Schaf "+(i+1)+": "+schaf.getName());
  System.out.println("Alter: "+schaf.getAlter());
}
		
//Berechnung des mittleren Herdenalters
int gesamt = 0;		
for (Schaf schaf: schafListe) {
  gesamt += schaf.getAlter();
}
double herdenalter = (double)gesamt/schafListe.size();
System.out.printf("Herdenalter: %.1f",herdenalter);

Automatisierte Ausgabe und Auswertung

In der for-Schleife wird über die size() Mehtode die Anzahl der Elemente abgefragt. Die get(..) Methode gibt das Element am angegebenen Index zurück.

for-each-Schleifen iterrieren über alle Daten­strukturen deren Elemente aufzählbar sind. In Java werden diese mit dem : Operator gekenn­zeichnet. Dabei ist Schaf der Datentyp des Elements und schaf das Elementobjekt welches aktuell betrachtet wird. Das Containerobjekt schafListe gehört zur Klasse ArrayList.

Aufgabe 7 Bücherei

Realisiere eine Bücherei in der Bücher mit Titel, Autor und Bewertung gespeichert werden sollen.

Bücherstapel
  1. Erstelle das Klassenpaar Book und Library.
  2. Füge zur Klasse Book die Attribute title, author, rating hinzu sowie entsprechende set- und get-Methoden.
  3. Füge zur Klasse Library eine ArrayList namens booklist hinzu und erzeuge drei Einträge wie bspw. booklist.add(new Book("Herr der Ringe", "J.R.R. Tolkien", 4.8));.
  4. Gib alle Einträge aus.
  5. Berechne die durchschnittliche Bewertung aller Bücher.

Aufgabe 8 Ordne zu

Ordne die korrekte Antwort zu.

Für Objekte der Klasse Schaf soll eine ArrayList erstellt werden.
Wähle eine Antwort.

  1. ArrayList box = new ArrayList();
  2. ArrayList<Schaf> box = ArrayList<Schaf>();
  3. ArrayList<Schaf> box = new ArrayList<Schaf>();
  4. ArrayList schafListe = new ArrayList();

Erkläre die Klasse ArrayList.
Wähle eine Antwort.

  1. die Klasse dient als Container für Arrays
  2. die Klasse erstellt eine Liste mit Arrays
  3. die Klasse erzeugt eine ungeordnete Liste
  4. die Klasse dient als Container für Objekte

Finde die Anzahl der Fehler im folgenden Quelltext.

import java.util.ArrayList;
public class Pizzamenu {
  public static void main(String[] args) {
    ArrayList menue = new ArrayList();
    menue1.add(new schaf());
  }
}

Finde die Anzahl der Fehler im folgenden Quelltext.

public class Pizza{
  private String name;
  public Pizza (String name, double preis){
    this.name = namen;
    this.preis = preis;
  }
}

In der ArrayList box sind 3 Schafe im Alter von jeweils 3 Jahren.
int i = 3;
for (Schaf schaf: box) {
  i += schaf.getAlter()-1;
}

Bestimme den Wert von i. Wähle eine Antwort.

  1. i hat den Wert 3
  2. i hat den Wert 12
  3. i hat den Wert 9
  4. der Quelltext ist fehlerhaft

In der ArrayList box sind zwei Schafe namens Emma und Berta.
for (int i=0; i<box.size(); i++) {
  Schaf schaf = box.get(i);
  System.out.print(schaf.getName());
}

Bestimme die Ausgabe. Wähle eine Antwort.

  1. EmmaBerta
  2. Emma, Berta
  3. Emma\nBerta
  4. weder noch

toString-Methode Objekte ausgeben

Mit Hilfe der toString-Methode kann man Objekte direkt ausgeben. Wir erstellen die Klasse Schaf mit zwei Konstrukoren und der toString-Mehtode. Diese Methode gibt eine Zeichenkette zurück und hat keine Übergabeparameterzwei Schafe mit Hilfe des Konstruktors

public class Schaf{
  private String name;
  private int alter;
  private double gewicht;

  //Konstruktor
  public Schaf (String n, int a, double g){
    this.name = n;
    this.alter = a;
    this.gewicht = g;
  }

  //toString-Methode
  public String toString (){
	return "Name: "+this.name+"\nAlter: "+this.alter;
  }
}
	
Zeichnung zweier Schafe

toString Methode aufrufen

In der Klasse Schafherde können wir nun die Objekte direkt ausgeben.

Man beachte das in der Methode printl(..), bei einem Objekt als Parameter, die toString-Methode aufgerufen wird.

	
public class Schafherde {
  public static void main(String[] args) {
    //Konstruktoraufruf mit Parameter
    Schaf s1 = new Schaf("Emma",20,50.0);
    Schaf s2 = new Schaf("Berta",8,30.0);

    //Objektinformationen ausgeben
    System.out.println(s1);
    System.out.println(s2);
  } 
}

Wortliste und Satzbausteine



das Objekt, -e Objekte haben Eigenschaften (engl. states) und Fähigkeiten (engl. behaviors). Diese werden in einer zugehörigen Klasse festgelegt.
die Klasse, -n Eine Klasse beschreibt, welche Eigenschaften und Fähigkeiten ein Objekt haben darf und stellt somit den Bauplan von Objekten bereit.
die Methode, -en Fähigkeiten von Objekten werden in Methoden beschrieben.
das Attribut, -en Eigenschaften von Objekten werden in Attributen (Variablen) festgelegt.
der Modifier, ~ legt die Sichtbarkeit von Eigenschaften und Fähigkeiten fest, also ob diese bspw. privat oder öffentlich zugänglich sind
die Kapse­lung, - die Sichtbarkeit der Attribute einer Klasse wird auf private gesetzt und so schirmt man die Attribute vom Zugriff aus anderen Klassen ab.
die set-, get-Me­thoden, - Methoden die die abgeschirmten Attribute setzen oder auslesen.
der Konstruk­tor, -en Methode welche die Initialisierung der Parameter bei Objekterzeugung ermöglicht
das Überla­den des Konstruk­tors Existieren mehrere Methoden gleichen Namens spricht man vom Überladen. So kann bspw. mit mehreren Konstruktoren ein unterschiedlicher Initialisierungsgrad erreicht werden.
die ArrayList eine Klasse, die als Container für eine Liste von Objekten dient