EDIT: Ich habe diese Frage beantwortet, weil es eine Menge Leute gibt, die das Programmieren lernen, und die meisten Antworten sind sehr technisch kompetent, aber sie sind nicht so einfach zu verstehen, wenn Sie ein Neuling sind. Wir waren alle Neulinge, also dachte ich, ich würde mich an einer neulingsfreundlicheren Antwort versuchen.
Die beiden wichtigsten sind Polymorphismus und Validierung. Auch wenn es nur eine blöde Datenstruktur ist.
Nehmen wir an, wir haben diese einfache Klasse:
public class Bottle {
public int amountOfWaterMl;
public int capacityMl;
}
Eine sehr einfache Klasse, die angibt, wie viel Flüssigkeit sich darin befindet und wie hoch ihre Kapazität ist (in Millilitern).
Was passiert, wenn ich:
Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;
Nun, Sie würden nicht erwarten, dass das funktioniert, oder? Sie möchten, dass es eine Art Überprüfung der geistigen Gesundheit gibt. Und schlimmer noch, was ist, wenn ich nie die maximale Kapazität angegeben habe? Oh je, wir haben ein Problem.
Aber es gibt noch ein anderes Problem. Was wäre, wenn Flaschen nur eine Art von Behälter wären? Was wäre, wenn wir mehrere Behälter hätten, alle mit Kapazitäten und Flüssigkeitsmengen gefüllt? Wenn wir nur eine Schnittstelle erstellen könnten, könnten wir den Rest unseres Programms diese Schnittstelle akzeptieren lassen, und Flaschen, Kanister und alle möglichen Dinge würden einfach austauschbar funktionieren. Wäre das nicht besser? Da Schnittstellen Methoden erfordern, ist dies auch eine gute Sache.
Wir würden am Ende so etwas wie:
public interface LiquidContainer {
public int getAmountMl();
public void setAmountMl(int amountMl);
public int getCapacityMl();
}
Großartig! Und jetzt ändern wir einfach die Flasche in diese:
public class Bottle extends LiquidContainer {
private int capacityMl;
private int amountFilledMl;
public Bottle(int capacityMl, int amountFilledMl) {
this.capacityMl = capacityMl;
this.amountFilledMl = amountFilledMl;
checkNotOverFlow();
}
public int getAmountMl() {
return amountFilledMl;
}
public void setAmountMl(int amountMl) {
this.amountFilled = amountMl;
checkNotOverFlow();
}
public int getCapacityMl() {
return capacityMl;
}
private void checkNotOverFlow() {
if(amountOfWaterMl > capacityMl) {
throw new BottleOverflowException();
}
}
Ich überlasse die Definition der BottleOverflowException als Übung dem Leser.
Beachten Sie nun, wie viel robuster dies ist. Wir können jetzt mit jeder Art von Container in unserem Code umgehen, indem wir LiquidContainer anstelle von Bottle akzeptieren. Und wie diese Flaschen mit solchen Dingen umgehen, kann unterschiedlich sein. Sie können Flaschen haben, die ihren Status auf die Festplatte schreiben, wenn sie sich ändern, oder Flaschen, die in SQL-Datenbanken oder GNU speichern, wissen was noch.
Und all dies kann verschiedene Möglichkeiten haben, mit verschiedenen Whoopsies umzugehen. Die Flasche prüft nur, ob sie überläuft, und löst eine RuntimeException aus. Aber das könnte falsch sein. (Es gibt eine nützliche Diskussion über die Fehlerbehandlung, aber ich halte sie hier absichtlich sehr einfach. Leute in Kommentaren werden wahrscheinlich auf die Mängel dieses simplen Ansatzes hinweisen .;))
Und ja, es scheint, als würden wir von einer sehr einfachen Idee schnell zu viel besseren Antworten gelangen.
Bitte beachten Sie auch, dass Sie die Kapazität einer Flasche nicht ändern können. Es ist jetzt in Stein gemeißelt. Sie können dies mit einem int tun, indem Sie es für endgültig erklären. Aber wenn dies eine Liste wäre, könnten Sie sie leeren, neue Dinge hinzufügen und so weiter. Sie können den Zugriff auf das Berühren der Innereien nicht einschränken.
Es gibt auch die dritte Sache, die nicht jeder angesprochen hat: Getter und Setter verwenden Methodenaufrufe. Das bedeutet, dass sie überall wie normale Methoden aussehen. Anstatt eine seltsame spezifische Syntax für DTOs und andere Dinge zu haben, haben Sie überall das Gleiche.