Clean Code: Was ist mit "level of abstraction" gemeint?

Saheeda

Mitglied
Hallo,

ich lese momentan "Clean Code" von Robert C. Martin.

Im 3. Kapitel gehts um Funktionen und woran man erkennt, dass sie nicht zu viel auf einmal machen:

Notice, that the three steps of the function are one level of abstraction below the stated name of the function [in the example]. We can describe the function by describing it as a brief TO paragraph.
[...]
If a function does only those steps that are one level below the stated name of the function, the function is doing one thing.
[...]
In order to make sure our functions are doing 'one thing', we need to make sure that the statements within our function are all at the same level of abstraction.
There are concepts [in the example] that are at a very high level of abstraction, such as getHTML() others are at an intermediate level of abstraction, such as String pagePathName = PathParser.render(pagePath) and still others, that are remarkably low level, such as .append("\n").

Mir ist trotz der Beispiele aber noch nicht so richtig klar, was mit Abstraktionsebene gemeint ist.
 
Hallo,

die von Spyke verlinkte Website gibt es übrigens auch als Windows 8 App. Da ist das Layout m. E. auch viel besser verwirklicht worden.

Zu deiner Frage: Ich glaube, der springende Punkt ist, dass eine Funktion nicht zu tief in die Abstraktionsebene greifen sollte. Tut sie das, sollte man diese Stellen auslagern. Später lassen sich dann die ausgelagerten Teile anderweitig nutzen, siehe folgendes Beispiel:
Javascript:
// SCHLECHT
function readCsv(file) {
  var fileHandle = openFile(file);
  var data = [];
  while (read(fileHandle)) {
    // ...
  }
  return data;
}

Obiger Code öffnet nicht nur eine Datei, sondern liest und parst sie auch.
Was ist, wenn wir die CSV-Daten als String im Arbeitsspeicher vorliegen haben? Dazu müssten wir eine neue Funktion schreiben.
Eine elegantere Lösung ist es, eine allgemeine Funktion für Strings im Arbeitsspeicher zu haben und nur noch eine, die aus einer Datei liest:
Javascript:
// GUT
function readCsvFromFile(file) {
  return readCsvFromString( readWholeFile(file) );
}

function readCsvFromString(str) {
  var data = [];
  while (read(fileHandle)) {
    // ...
  }
  return data;
}

Wenn du du hier die Erklärung mit den "Level of Abstractions" analog anwendest, dann könntest du sie wie folgt ausdrücken:
Javascript:
Erster Code:
===============
readCsvFromFile --(--> readCsvFromString --)--> Zugriff auf String

Zweiter Code:
===============
readCsvFromFile --> readCsvFromString
readCsvFromString --> Zugriff auf String

Man kann es noch eine Stufe weitertreiben, indem man nicht von Strings spricht, sondern von Streams. Diese können bspw. von einem String ausgehen, könnten man auch die Daten von einer HTTP-Adresse zurückliefern:
Javascript:
Stream-Code
===============
readCsvFromFile --> readCsvFromStream
readCsvFromStream --> Zugriff auf Stream
Stream --> String (oder Rückgabe bei einer Anfrage an einen Server)
 
Könnte man dann auch von "Verantwortlichkeiten" oder "Aufgaben" reden, anstatt von Abstraktionsebenen?


Inzwischen bin ich beim Thema Klassendesign angekommen und habe schon wieder das nächste gefunden, was mich leicht irritiert.
In so ziemlich jedem Tutorial (darunter auch Vorlesungen/Skripte ausm Grundstudium), das ich bisher gefunden habe, wurde der in der GUI benötigte Code direkt in der "GUI-Klasse" erzeugt. Nirgendwo kam auch nur der Hinweis, dass das eigentlich schlechtes Klassendesign wäre, egal, wie komplex die Beispiele waren.

Im Buch ist hingegen vom Single-Responsibility-Principle die Rede. Es wird sogar explizit gesagt, dass es schlechter Stil wäre, GUI-Klasse und "eigentlichen Code" so zu vermischen:

The seemingly small SuperDashboard class in Listing 10-2 has two reasons to change.
First, it tracks version information that would seemingly need to be updated every time the
software gets shipped. Second, it manages Java Swing components (it is a derivative of
JFrame, the Swing representation of a top-level GUI window).

Was ich meine ist das:
Code:
Class GUI{

    private button_click(object sender, EventArgs e)
    {
        Task.doSomething();
   
    }
}

class Task
{
    private static void doSomething()
    {
       [...]
    }
}

vs. das:
Code:
Class GUI{

    private button_click(object sender, EventArgs e)
    {
        [......]
   
    }
}
 
Könnte man dann auch von "Verantwortlichkeiten" oder "Aufgaben" reden, anstatt von Abstraktionsebenen?
Wenn du so willst, ja. Aber ich finde es wichtiger, das Prinzip zu verstehen, als sich zu zweingen, irgendwelche Namen auswendigzulernen.

Zum Rest: Natürlich ist es schlecht, die Business-Logik (bei dir Task) mit dem Programmteil, der für die Anzeige ("View") zuständig ist, zu vermischen.
Warum das so viele vermischen?
- Sie haben es nicht besser gelernt.
- Sie schreiben nur ein Beispiel / schnellen Prototyp / nehmen an einem Code Golf Wettbewerb teil.

Zum Thema GUI <--> Business Logik könntest du dir auch mal das MVC-Prinzip anschauen.
 
Kostenlose Tutorials werden auch meist in der Freizeit geschrieben,
da gehts dem Schöpfer eher drum sein Wissen anderen näher zu bringen, der achtet dann nicht auf design patterns.

Und da kommts dann drauf an in der Fülle der Menge die besten zu finden.

Irgendwo wurde erst ein youtube Video zu C# empfohlen, das ich mir selbst nicht mal 10 min. anschauen konnte.

Ansonsten business logik von GUI sollte man immer trennen.
Allerdings gibts auch Ausnahmen wo ich mich auch schonmal nicht dran gehalten habe (einfach weils sich nicht lohnte oder nicht anders ging).
Wichtig ist, die Ausnahmen sollten nicht zur Regel werden.
 
Zurück