Duplikate: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
{{ | {{CategoryBlock | ||
| | |Baustelle=Ja | ||
| | |Java Grundlagen=Nein | ||
| | |Organisation=Nein | ||
|Programmierstil=Nein | |||
|Bewertungsrichtlinie=Ja | |||
|blattAnnotation=2 | |||
|blattAbzug=3 | |||
}} | |||
{{Inhaltsblock | |||
|vorher==== 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: | Codewiederholung oder Codekopien sind ausnahmslos zu vermeiden. Z.B. durch das Einführen von privaten Hilfsmethoden. Codewiederholung hat im Wesentlichen drei Nachteile: | ||
| Zeile 18: | Zeile 26: | ||
Warum den ganzen Aufwand überhaupt? | Warum den ganzen Aufwand überhaupt? | ||
Stellen wir uns folgendes Beispiel vor: | Stellen wir uns folgendes Beispiel vor: | ||
| | }} | ||
| | {{Inhaltsblock | ||
public class Heightmap { | |color=red | ||
|vorher=Negativbeispiel: | |||
|Beispiel=public class Heightmap { | |||
private double [][] mapData; | private double [][] mapData; | ||
| Zeile 44: | Zeile 54: | ||
} | } | ||
} | } | ||
|beispielname=DuplikateBad1 | |||
Was passiert hier, wenn wir die Indizierung der Daten geändert werden muss. Man müsste alle Codestellen finden, die das betrifft. | |nachher=Was passiert hier, wenn wir die Indizierung der Daten geändert werden muss. Man müsste alle Codestellen finden, die das betrifft. | ||
}} | |||
{{Inhaltsblock | |||
|color=red | |||
public class DigitalClock { | |Beispiel=public class DigitalClock { | ||
private int time; | private int time; | ||
private final static int S_IN_DAY = 86400; | private final static int S_IN_DAY = 86400; | ||
| Zeile 80: | Zeile 90: | ||
} | } | ||
} | } | ||
|beispielname=DuplikateBad2 | |||
|nachher=Wir haben also verschiedene Uhren. Von den 4 implementierten Methoden der Klassen sind 3 identisch. | |||
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?''' | '''Was, wenn eine davon fehlerhaft ist oder aus einem anderen Grund geändert werden soll?''' | ||
| Zeile 90: | Zeile 99: | ||
Wir verbessern: | Wir verbessern: | ||
| | }} | ||
public class Heightmap { | {{Inhaltsblock | ||
|color=green | |||
|vorher=Positivbeispiel: | |||
|Beispiel=public class Heightmap { | |||
private double [][] mapData; | private double [][] mapData; | ||
| Zeile 119: | Zeile 131: | ||
} | } | ||
} | } | ||
|beispielname=DuplikateGood1 | |||
Jetzt befindet sich das Prüfen der Position an genau einer Stelle: In der Methode inBounds. | |nachher=Jetzt befindet sich das Prüfen der Position an genau einer Stelle: In der Methode inBounds. | ||
}} | |||
{{Inhaltsblock | |||
|color=green | |||
public abstract class Clock { | |Beispiel=public abstract class Clock { | ||
protected int time; | protected int time; | ||
private final static int SECONDS_IN_DAY = 86400; | private final static int SECONDS_IN_DAY = 86400; | ||
| Zeile 162: | Zeile 174: | ||
} | } | ||
} | } | ||
|beispielname=DuplikateGood2 | |||
|nachher=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 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. | 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. | Ändert sich jetzt in einer der Methoden etwas, müssen wir jede Methode nur genau einmal finden und ändern. | ||
}} | }} | ||
Aktuelle Version vom 13. Oktober 2025, 14:32 Uhr
| 🚧 | 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:
- 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.
- 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.
- 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();
}
}
}
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;
}
}
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.