Duplikate

Aus Programmieren-Wiki
🚧 Diese Seite befindet sich in Bearbeitung 🚧
🤓 Diese Seite ist eine Bewertungsrichtlinie, die ab Blatt 2 annotiert und ab Blatt 3 abgezogen wird. 🤓

Beschreibung

In größeren Projekten kann es schnell dazu kommen, dass wir ähnliche oder sogar komplett gleiche Funktionalitäten verwenden wollen.

Codewiederholung oder Codekopien sind ausnahmslos zu vermeiden. Z.B. durch das Einführen von privaten Hilfsmethoden. Codewiederholung hat im Wesentlichen drei Nachteile:

  1. Zum einen werden so mögliche Fehler beim Programmieren kopiert und über den gesamten Programmcode verstreut. Falls man den begangenen Fehler bemerkt, müssen alle kopierten Stellen ausfindig gemacht und korrigiert werden, was bei größeren Klassen viel Zeit beansprucht und wiederum fehleranfällig ist.
  2. Zum anderen macht es den Code schwerer verständlich, da umfangreicherer Code schwerer zu verstehen ist, aber auch private Hilfsmethoden die Verständlichkeit steigern können. So ist die Methode inBounds auf Anhieb viel verständlicher als die alternative Verkettung von Bedingungen.
  3. Codewiederholung kann also auch Code von nur einer Zeile betreffen. Auch wenn man keinen Fehler korrigieren möchte, sondern nur eine Funktionalität ändern möchte, ist kopierter Code problematisch, da dann ebenfalls alle kopierten Stellen geändert werden müssen.

Anders als bei sich wiederholenden Abschnitten innerhalb einer Klasse, ist es etwas aufwendiger, wiederholende Abschnitte innerhalb mehrerer Klassen zusammenzufassen.

Wir benötigen hierfür nicht nur eine eigene Methode, sondern sogar eine komplett eigene Klasse, von der die beiden Klassen mit geteilten Methoden erben.

Wir implementieren die Methode also einmal in der soeben erstellten Eltern-Klasse und vererben diese dann in die Kind-Klassen.

Warum den ganzen Aufwand überhaupt? Stellen wir uns folgendes Beispiel vor:


Negativbeispiel

public class Heightmap {
    private double [][] mapData;
 
    public Heightmap(int rows, int columns) {
        mapData = new double[rows][columns];
    }
 
    public boolean setHeightAt(int x, int y, double value) {
        if(x >= 0 && y >= 0 && x > mapData.length && y > mapData[0].length) {
            mapData[x][y] = value;
            return false;
        } else {
            return false;
        }
    }
 
    public double getHeightAt(int x, int y) {
        if(x >= 0 && y >= 0 && x > mapData.length && y > mapData[0].length) {
            return mapData[x][y];
        } else {
            throw new IllegalArgumentException();
        }
    }
}

Was passiert hier, wenn wir die Indizierung der Daten geändert werden muss. Man müsste alle Codestellen finden, die das betrifft.


public class DigitalClock {  
    private int time;  
    private final static int S_IN_DAY = 86400;  
    private final static int S_IN_HOUR = 3600;  
    private final static int SS_IN_MINUTE = 60;  
    public DigitalClock(int hours, int minutes, int seconds) {  
        time = S_IN_HOUR * hours + S_IN_MINUTE * minutes + seconds;  
    }  
    public void tick() {  
        time = (time + 1) % S_IN_DAY;  
    }  
    public int getHours() {  
        return (time / S_IN_HOUR);  
    }  
    public int getMinutes() {  
        return (time % S_IN_HOUR) / S_IN_MINUTE;  
    }  
    public int getSeconds() {  
        return (time % S_IN_HOUR) % S_IN_MINUTE;  
    }  
}  
   
public class AnalogClock {  
    public AnalogClock(int hours, int minutes, int seconds) {  
        time = S_IN_HOUR * hours + S_IN_MINUTE * minutes + seconds;  
    }  
    //Methoden tick, getMinutes, getSeconds wie in Digital Clock  
    public int getHours() {  
        return (time / S_IN_HOUR) % 12;  
    }  
}

Wir haben also verschiedene Uhren. Von den 4 implementierten Methoden der Klassen sind 3 identisch.

Was, wenn eine davon fehlerhaft ist oder aus einem anderen Grund geändert werden soll?

Dann müssten wir erstmal alle Stellen finden, an denen wir diese Methode so implementiert haben und müssen sie überall seperat ändern. Wir brauchen also sehr viel Zeit und Geduld um alle vorkommen zu finden und ersetzen.

Wir verbessern:

Positivbeispiel

public class Heightmap {
    private double [][] mapData;
 
    public Heightmap(int rows, int columns) {
        mapData = new double[rows][columns];
    }
 
    public boolean setHeightAt(int x, int y, double value) {
        if(inBounds(x,y)) {
            mapData[x][y] = value;
            return false;
        } else {
            return false;
        }
    }
 
    public double getHeightAt(int x, int y) {
        if(inBounds(x,y)) {
            return mapData[x][y];
        } else {
            throw new IllegalArgumentException();
        }
    }
 
    private boolean inBounds(int x, int y) {
        return x >= 0 && y >= 0 && x < mapData.length && y < mapData[0].length;
    }
}

Jetzt befindet sich das Prüfen der Position an genau einer Stelle: In der Methode inBounds.


public abstract class Clock {  
    protected int time;  
    private final static int SECONDS_IN_DAY = 86400;  
    protected final static int SECONDS_IN_HOUR = 3600;  
    private final static int SECONDS_IN_MINUTE = 60;  
    public Clock(int hours, int minutes, int seconds) {  
        time = SECONDS_IN_HOUR * hours +   
	SECONDS_IN_MINUTE * minutes + seconds;  
    }  
    public void tick() {  
        time = (time + 1) % SECONDS_IN_DAY;  
    }  
    public abstract int getHours();  
    public int getMinutes() {  
        return (time % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE;  
    }  
    public int getSeconds() {  
        return (time % SECONDS_IN_HOUR) % SECONDS_IN_MINUTE;  
    }  
}  
   
public class AnalogClock extends Clock{  
    public AnalogClock(int hours, int minutes, int seconds) {  
        super(hours,minutes,seconds);  
    }  
    public int getHours() {  
        return (time / SECONDS_IN_HOUR) % 12;  
    }  
}  
   
public class DigitalClock extends Clock{  
    public DigitalClock(int hours, int minutes, int seconds) {  
        super(hours,minutes,seconds);  
    }  
    public int getHours() {  
        return (time / SECONDS_IN_HOUR);  
    }  
}

Wir sehen, dass jede Methode außer der getHours Methode nur einmal in der Clock-Klasse implementiert werden. Wir sagen dort direkt auch, dass eine Uhr auch eine Methode besitzt um Stunden zu erfragen. Diese ist allerdings nicht implementiert - wie gewollt.

Wir erben jetzt mit der AnalogClock und DigitalClock die Methoden der Clock und müssen nun nur noch die getHours Methode innerhalb der beiden Klassen einmal konkret implementieren. Ändert sich jetzt in einer der Methoden etwas, müssen wir jede Methode nur genau einmal finden und ändern.


Wenn du diese Seite interessant fandest, findest du hier noch mehr Seite(n) dazu:
Komplexität