Allgemeine Verständnisfrage zu Methoden

Hallöchen!

Nehmen wir an, ich habe ein Programm, dass zeilenweise ein File einliest. Die Zeilen enthalten Zahlen, mit denen ich rechnen will, z.B: ALLE Zahlen zusammenzählen, NUR die Zahlen einer Zeile zusammenzählen, Zeilen zählen, Gesamtsumme der Zahlen durch Zeilen teilen, aus jeder Zeile die eine gerade Summ enthält e die Wurzel ziehen usw. Lauter Kleinigkeiten also, die alle irgendwie voneinander abhängen.

Was mir nicht einleuchtet: wann benötige ich eigene Methoden? Das File mit einer Methode einzulesen, ok, das kann ich nachvollziehen.
Aber sollten all die anderen Funktionen auch eigene Methoden bekommen (quasi: zähleZeilen, addiereZahlen, zieheWurzeln, …) oder mehrere zu einer zusammenfassen…? Wie erkenne ich, wann es tatsächlich Sinn macht, eine eigene Methode für eine Aufgabe zu schreiben?

Das mag als eine blöde Frage erscheinen, aber die gibt es ja bekanntlich nicht :wink:

Bin auf Antworten gespannt, schönen Tag noch!

Blöd ist die nicht. Im Gegenteil. Das ist eine der Fragen, die man sich ständig stellt. Gleichzeitig ist sie so allgemein, dass man schwer eine konkrete Antwort geben kann. Die Frage, wann man eine eigene Methode benötigt ist aber nochmal eine ganz andere als die, wann eine eigene Methode sinnvoll ist :wink:

Im allgemeinen sollte man Methoden ziemlich „feingranular“ schreiben. Also vereinfacht gesagt: Viele sinnvolle, kurze Methoden mit sinnvollen Namen. Es gibt so Faustregeln, dass eine Methode „nicht mehr als so-und-so-viele Zeilen haben sollte“ - man muss das wohl von Fall zu Fall unterscheiden, aber wenn eine Methode nicht mehr komplett auf den Bildschirm passt, sollte man sich schon fragen, ob man die nicht aufsplitten kann.

Wichtiger als die Länge sind aber IMHO andere Dinge: Die Methode sollte eine klar definierte Aufgabe haben, und vernünftig strukturiert sein. Wenn im Methodennamen das Wort „Und“/„And“ vorkommt, ist das ein Zeichen, dass etwas falsch sein könnte :wink: z.B. „leseZeilenUndSummiereSieAuf“ - Besser: „leseZeilen“ und „summiereAuf“. Dabei stellt sich sofort die Frage nach der Struktur, bzw. Signatur der Methode: Welche Parameter bekommt sie übergeben, und was liefert sie zurück?

Als übergeordnete, wichtige Frage stellt sich, welche access modifier man verwendet (public, protected, default, private) - die allgemeine Regel, dass man alles so privat wie möglich machen sollte gilt auch hier.

An dem konkreten Beispiel: Wenn es eine Methoden zum (allgemeinen) „Einlesen“ gibt, was gibt die dann zurück? Einen float[][]-Array? Oder eine List<List>? Oder eine Instanz einer eigenen Klasse wie z.B. „Datensatz“?

Klasse, danke für deine flinke Antwort!
Mir fehlt noch die richtige Herangehensweise an das Programmieren. Dein Ansatz “so kurz und sinnvoll wie möglich und nur eine Aufgabe” hilft mir da schon sehr weiter. Das ist zumindest etwas, an das ich mich halten kann.
Trotzdem denke ich mir dann: um z.B. Zeilen aufzusummieren reicht ja im Prinzip (mal abgesehen vom Methodenkopf, Initiierung der Variablen) “zeile++;” aus. Da wird der Quelltext (und die Schreib- und Denkarbeit, um die geht es mir eigentlich mehr als um die Länge) zig Mal so lang für eine Sache, die an sich nur ein, zwei Zeilen benötigt. Und dann muss ich mich ja auch darum kümmern, wie ich den Wert für andere Methoden verfügbar mache und eben die Dinge die du erwähnt hast. Und dann erscheint mir der Aufwand einer eigenen Methode im Vergleich zum Einbinden in den restlichen Quellcode zu groß.
Was das Einlesen betrifft, hätte ich einen String eingelesen, den ich dann für die Berechnung (z.B.) in int umgewandelt hätte. Sowas ist kein Problem, aber sich Gedanken um die passende Methode zu machen, was sie zurückliefert, dass ich sie explizit ansprechen muss und vor allem das “wie”… mmh…
Mag sein, dass das daran liegt, dass ich noch Anfängerin bin und mir deswegen das schreiben einer Methode nicht so einfach von den Fingern geht… gut, dass wird die Zeit (hoffentlich ;P) bringen.

Dass es für das Thema keine allgemein gültige Antwort gibt, war mir klar. Aber solche Stohhalme wie du sie mir geliefert hast, sind schonmal ein guter Anfang für mich; hoffentlich baue ich sie zu festen Brücken aus (der Spruch ist doof aber was soll’s, ich hab grad meine kreativen fünf Minuten ;)).

Ja, es ist tatsächlich so, dass die eigentliche Arbeit (und das, wofür manche Programmierer so viel Geld bekommen) nicht das eigentliche „Hinschreiben des Codes“ ist, sondern sich im Vorfeld „auf höherer Ebene“ klar zu machen, wie man etwas schreiben wird. Das kann man sehr weit treiben, es gibt unterschiedliche Philosophien und ganze Wissenschaften über „das richtige Programmierparadigma“, aber speziell am Anfang kann man solche Fragen mal außen vor lassen, und sich erstmal klarmachen, wie Methoden (und Klassen) aussehen sollten.

Anhand der Beispiele, die du angedeutet hast (und die du für konkretere Hinweise noch etwas weiter ausführen müßtest) :
aus jeder Zeile die eine gerade Summ enthält e die Wurzel ziehen

Man könnte natürlich etwas schreiben, was so aussieht:

public void computeSquareRootsOfEveryRowThatHasAnEvenSum(File file) throws IOException
{
    BufferedReader br = new BufferedReader(... new FileInputStream(file) ...);
    ...
    int numbers[][] = new int[1000][10];
    while (line != null) 
    {
        ...
        numbers**[j] = Integer.parseInt(token);
    }
    ...
    int sums[] = new int[1000];
    for (int i=0; i<1000; i++)
    {
        for (int j=0; j<10; j++)
        {
            sums** += numbers[j];
        }
    }
    ...
    for (int i=0; i<1000; i++)
    {
        if (sums** % 2 == 0)
        {
            for (int j=0; j<10; j++)
            {
                System.out.println(Math.sqrt(numbers**[j];
            }
        }
    }
}

Wenn du das jetzt nicht Zeile für Zeile nachvollzogen hast, ist das ja schon ein deutliches Zeichen dafür, dass das nicht so gut ist :wink: (und noch aus vielen, vielen anderen Gründen). Wenn man so eine „komplizierte“ Aufgabe so detailliert und ohne Hilfsmethoden hinschreibt, kann man sie kaum noch nachvollziehen. Besser wäre es doch, wenn sie zum Beispiel(!) ungefähr(!) so aussehen würde

public double[][] computeSquareRootsOfEveryRowThatHasAnEvenSum(String fileName)
{
    int numbers[][] = readFile(fileName);
    int sums[] = computeSums(numbers);
    double squareRoots[][] = new double[sums][];
    for (int i=0; i<sums.length i++)
    {
        if (isEven(sums**)) 
        {
            squareRoots** = computeSquareRoots(numbers**);
        }
    }
}

(Nur zur Verdeutlichung, macht so nicht viel Sinn)

Das ist durch die sprechenden Methodennamen schneller und leichter lesbar, fast wie normaler englischer Text. Zusätzlich hat das den Vorteil, dass man die „vielen“ kleinen Methoden wie Bausteine zusammensetzen und immer wieder verwenden kann. Z.B. könnte es eine Methode geben
public static int computeSum(int array[]) {…}
die die Summe eines Arrays berechnet, und diese könnte dann verwendet werden innerhalb einer Methode
public static int[] computeSums(int array[][]) {…}
um die Summen der einzelnen Zeilen eines 2D-Arrays zu berechnen, und diese Methode wird ja im oben anskizzierten Code verwendet - aber vielleicht auch in anderen, ähnlichen Funktionen, denn schließlich ist sie sehr allgemein und erfüllt nur eine kleine, spezifische Teilaufgabe.

Wenn man sie wiederverwenden will wird die Frage, wie die Methodensignatur aussehen soll aber besonders wichtig. Eine kleine private Hilfsmethode kann man mit weniger Nachdenken „mal schnell“ hinschreiben, aber wenn sie „public“ ist und überall verwendet werden soll, sollte sie besser durchdacht sein.

Das ganze bezieht sich bisher aber nur auf Methoden - wenn man das ganze im Kontext von Klassen sieht, kommen nochmal etliche Aspekte dazu, die weit darüber hinausgehen. Für die Aufgaben, die du angedeutet hast, könnte man auch auf Ebene der Klassen (und interfaces) die man definiert in bezug auf die Abstraktion sehr weit gehen. Und sogar in bezug auf die Sprache: Es gibt funktional angehauchte Sprachen (wie Groovy oder Scala) bei denen man diese Methode mit… vielleicht 5 Zeilen oder so schreiben könnte…
Aber alles der Reihe nach :wink:

Danke für die Mühe, die du dir machst :slight_smile:

Nachdem ich jetzt noch ein wenig herumprobiert habe, ist mir das alles schon noch ein wenig klarer geworden. Und so wie in deinem Beispiel habe ich auch an meinen eigenen Programmen gemerkt, dass sie schnell unübersichtlich werden, wenn ich sie nicht in Methoden untergliedere. Und man ist da ja doch sehr schnell bei mehr als 100 Zeilen…
Der erste Code ist schon deutlich schwieriger zu verstehen, aber ich gehe dann immer erstmal davon aus dass ich schlichtweg zu blöd dafür bin; der Programmierer wird sich schon was dabei gedacht haben :wink:
Also ich denke mit Methoden werde ich es tatsächlich so halten wie du es beschrieben hast. Dann muss ich auch nicht immer das Rad neu erfinden bzw. den richtigen Abschnitt aus einem alten Code heraussuchen, wenn ich eine bestimmte Aufgabe lösen muss und weiß: irgendwo habe ich das doch schonmal gemacht… . Habe mich am Anfang wohl doch etwas verschätzt; die Mehrarbeit die ich beim Erstellen einer neuen Methode habe, die spare ich mir nachher unter Umständen zig-fach wieder ein.

Aber mit dem Thema Klassen hast du mir jetzt neue Fragen aufgeworfen.
Klassen hatte ich eigentlich immer als das Gesamtprojekt an sich gesehen: wenn ich ein Programm schreibe, enthält die Klasse (was ja die *.java- und *.class-Datei ist) alles, was dieses Programm können muss (die einzelnen Funktionen in Methoden untergliedert :wink: ). Habe ich da einen Denkfehler? Ich meine, ansonsten könnte ich ja theoretisch auch jede Methode als eigene Klasse definieren!?
Woran macht der gemeine Programmierer fest, wann er eine Methode oder Klasse erstellt? Gibt es da Kriterien oder frei Schnauze? Gewohnheit?
Ich weiß, schonwieder sowas schrecklich Allgemeines…
Irgendwie fehlt mir dazu noch das Verständnis (und ich habe wirklich schon viel gelesen, ist ja nicht so dass ich hierherkomme und alles auf dem Silbertablett serviert wissen möchte), ich finde das alles sehr abstrakt. Und das was ich bisher so gelesen habe, beschäftigt sich zwar mit dem „sprachlichen“ (wie sieht eine Klasse / Methode aus), aber was genau sie ausmacht, wann man was verwendet, das finde ich nirgendwo.
Ich habe das Gefühl es gibt da ungeschriebene Gesetze über die keiner redet, aber von denen jeder weiß… lol.

Schönes Restwochenende noch!

Ja, wenn es wirklich NUR um Methoden geht, könnte man das ganze noch mit relativ einfach-pragmatischen Regeln abdecken: ~„Wenn man einen Block mit 10 Zeilen an zwei Stellen braucht, dann schreibt man den Code in eine Methode, und ruft die dann an diesen beiden Stellen auf“. Ein bißchen Nachdenken, damit die Methodensignatur „vernünftig“ und allgemeingültig ist, aber damit kann man schon viel abdecken.

**
Aber mit dem Thema Klassen hast du mir jetzt neue Fragen aufgeworfen.

Gibt es da Kriterien oder frei Schnauze? Gewohnheit?
Ich weiß, schonwieder sowas schrecklich Allgemeines…
**

Ja, da könnte man wirklich Seitenlang drüber referieren, und im Gegensatz zu Methoden können die Erwägungen da schnell sehr Philosophisch werden. Eine (an Anfänger gerichtete) Beschreibung, die ich mal irgendwann gelesen hatte, war etwa so: „Schreibe als Text auf, was dein Programm können soll. Die Substantive sind die Klassen, und die Verben sind die Methoden“. Das kann natürlich nur als Richtschnur gelten. Irgendwann werden Programme so komplex und abstrakt, dass das nicht mehr 1:1 funktioniert, aber für den Anfang ist es ganz OK.

Der allgemeine, „high-level“ Entwurf von Klassenstrukturen ist eine Wissenschaft für sich, es gibt da unterschiedliche Ansätze. Manche beschreiben ihre Klassen mit UML, um einen Überblick zu bekommen (ich persönlich finde, dass das nur für oberflächliche Skizzen auf einem Whiteboard Sinn macht, aber andere sehen das anders). „Regeln“ gibt es dafür nicht direkt, viel hängt auch mi Erfahrung zusammen. Aber indirekte Regeln gibt es schon, nämlich „Best practices“, die beschreiben, welche allgemeinen Ziele man verfolgen sollte, und wie man auf diese Ziele hinarbeiten kann. Da kann man sich mal http://de.wikipedia.org/wiki/Entwurfsmuster ansehen - von einigen werden die schon fast sklavisch zur Religion erhoben, man sollte sie nicht überbewerten, aber sie liefern auf jeden Fall einige Ansätze.

Allgemeine Informationen zu best practices findet man z.B. auch auf http://www.clean-code-developer.de/ - und in der ersten Regel, „DRY“, findest du vielleicht das wieder, was oben zu den Methoden steht :wink: