AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Guter Code ist lesbarer Code
Tutorial durchsuchen
Ansicht
Themen-Optionen

Guter Code ist lesbarer Code

Ein Tutorial von Luckie · begonnen am 8. Apr 2007 · letzter Beitrag vom 9. Apr 2007
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Luckie
Luckie
Registriert seit: 29. Mai 2002
Dies ist kein Tutorial in dem Sinne, als dass ich konkret auf ein Problem oder ein Thema eingehe. Viel mehr geht es um etwas grundsätzliches was das Thema Code-Design betrifft: Die Lesbarkeit von Quellcode.

Ich programmiere schon länger und habe schon so manche Zeile an Code geschrieben und gelesen. Aber im Laufe seiner Laufbahn als Programmierer wird man immer wieder über ein Problem stolpern, welches einen dazu veranlasst wieder etwas dazu zu lernen, auch wenn es nur etwas Banales ist wie Enumerationen.

Ich hatte folgendes Problem: Es gab eine Variable oder Routine, die nur zwei Zustände annehmen können sollte, die Ausrichtung einer Seitenzahl auf einer Seite, entweder mittig auf der Seite zentriert oder am äusseren Rand der Seite. Man kann hiermit ganzen Zahlen arbeiten: 0 für mittig zentriert und 1 für am äusseren Rand. Das ist ist aber nicht ganz befriedigen, den nach einiger Zeit weiß man nicht mehr, welche Zahl für was steht. Und ein Aussenstehender weiß es schon gar nicht. Diese Lösung wäre so gut wie unlesbar, kaum wartbar und sehr fehleranfällig, denn was passiert, wenn jemand zwei oder drei oder minus vier übergibt? Wir haben also zwei Probleme: 1. Lesbarkeit des Codes und 2. Anfälligkeit für Fehler.

Betrachten wir Punkt eins und nehmen uns mal eine Windows Funktion vor: CreateEvent:

Code:
HANDLE WINAPI CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
);
Problematisch sind hier die zwei Boolean-Parameter. Es wird nicht deutlich, was ein Setzen auf Wahr oder Falsch bewirkt. Wird der erste auf Wahr gesetzt, habe ich dann ein manuelles Zurücksetzen oder nicht? Und was ist der Unterschied zwischen einem wahren und einem falschen Initialisierungstatus? Da hilft es nur in der Hilfe nachzugucken. Eine kleine Hilfe ist schon die Code-Vervollständigung der IDE, aber die hat man auch nicht immer zur Hand (in einem Forum, einer E-Mail, wenn man den Code in einem einfachen Editor betrachtet, als Ausdruck oder mit einem Overheadprojektor). Und oft Mals schreibt man den Code nicht selber, sondern liest ihn nur. Besser wären in diesem Fall Aufzählungstypen mit zum Beispiel den Werten: MANUEL_RESET und AUTO_RESET, dann wäre beim Lesen alles klar. Beim zweiten Boolean-Parameter könnte man sinngemäß verfahren.

Kommen wir zurück zu meinem konkreten Problem. Es hat etwas gedauert, aber ich bin dann doch auf eine gute Lösung gekommen. Ich habe mich nachher etwas geärgert, dass ich so lange dafür gebraucht habe, obwohl ich sie die ganze Zeit vor der Nase hatte: Die Lösung ist eine Aufzählung. Eine Aufzählung wie sie zum Beispiel von der VCL benutzt wird, um einen Absatz in einem TMemo auszurichten: taCenter, taLeftJustify, taRightJustify. Mit der Aufzählung hat man einen eigenen Datentypen mit einem vorgegebenen Bereich und benannten Konstanten, wenn man so will. Eine Lösung in Delphi sähe also so aus:

Delphi-Quellcode:
type
  TPageNumberAlignment = (paCenter, paLeft, paRight);

procedure SetPageNumberAlignment(Alignment: TPageNumberAlignment);
begin
  case Alignment of
    paCenter: ShowMessage('zentriert');
    paLeft: ShowMessage('links');
    paRight: ShowMessage('rechts');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetPageNumberAlignment(paCenter);
end;
Damit haben wir mit einem Schlag unser beiden Probleme gelöst: Der Code ist lesbar und er ist nicht mehr fehleranfällig, da jetzt nur genau drei Werte eines bestimmten Datentyps übergeben werden können.

Man sieht also dass ein lesbarer Code in den meisten Fällen auch guten Code erzeugt. Deswegen der Grundsatz: "Design for readability". Oder in etwa auf deutsch: "Entwerfen für die Lesbarkeit". Wenn man sich an diesen Grundsatz orientiert beim Programmieren, ergibt sich ein zweiter Grundsatz von alleine: "So wenig Kommentare wie möglich, so viele wie nötig." Man sollte also nur das kommentieren, was nicht aus dem Code ersichtlich ist. Das Inkrementieren einer Variablen zu kommentieren ist überflüssig, wenn man den Code liest, sieht man ja, was passiert. Während hingegen das warum eventuell Sinn ergeben könnte.

Bleibt noch die Frage zu klären, wie man denn lesbaren, guten Code schreibt. Ich denke, das kann man nicht richtig lernen. da hilf wohl wirklich nur Erfahrung, die man sich über Jahre aneignet und das Lesen und Studieren von fremden Code. Obwohl, manchmal muss man gar nicht in die ferne schweifen. Oftmals reicht es schon aus, wenn man sich alten Code von sich selber nach ein, zwei Jahren wieder mal anguckt. Versteht man ihn ohne lange darüber nachzudenken, wie er funktioniert, was er macht und warum man das damals so und nicht anders gelöst hat, war es guter Code. Andernfalls könnte er etwas Überarbeitung gebrauchen. Eventuell hilft ja auch der Ratschlag von Donald Edwin Knuth:
Zitat:
Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.
Oder um es mit den Worten des Pascal Erfinders, Niklaus Wirth, zu sagen:
Zitat:
Programs should be written and polished until they acquire publication quality.
Link zum Artikel auf meiner Homepage: Guter Code ist lesbarer Code
Und im Anhang das ganze noch mal zum Ausdrucken als PDF.
Angehängte Dateien
Dateityp: pdf gutercode_155.pdf (75,6 KB, 61x aufgerufen)
Ein Teil meines Codes würde euch verunsichern.
 
Ghostwalker

 
Delphi 10.3 Rio
 
#2
  Alt 8. Apr 2007, 06:53
Da kann ich nur voll und ganz zustimmen.

Vielleicht noch eine kleine Anmerkung zum Thema Kommentare. Aus meiner Erfahrung heraus kann ein zuviel an Kommentaren die Lesbarkeit eines Codes zerstören. Insbesondere wenn es sich, wie Luckie schon geschrieben hat, es sich um eher unwichtige Kommentare handelt.
Uwe
  Mit Zitat antworten Zitat
Benutzerbild von hanselmansel
hanselmansel

 
Delphi 2009 Enterprise
 
#3
  Alt 8. Apr 2007, 08:15
Hi Luckie,

das soll jetzt keine Kritik an deinem Aufsatz werden, sondern eher eine weiterführende Frage. Ich finde es sehr gut, wenn du guten und lesbaren Code schreiben möchtest, unter anderem auch deshalb, damit fremde Programmierer deinen Code verstehen, wenn sie ihn lesen müssen. Die Lösung mit einem Aufzählungstyp finde ich auch sehr gelungen, da die Konstanten selbstredende Namen haben.

Wie würdest du es lösen, die anderen Konstanten dieses Aufzählungstyps dem fremden Programmierer mitzuteilen?
Ich meine, wenn du den Befehl SetPageNumberAlignment(paCenter); schreibst, kann sich jeder normal denkende Programmierer erschließen, dass es diese Einstellung auch für links und rechts gibt. Was würdest du allerdings tun, wenn es eine Funktion mit Parametern ist, die sich nicht auf Anhieb erschließen lassen?

Man kann ja eigentlich nicht davon ausgehen, dass dem fremden Programmierer deine Typdeklaration immer als Source vorliegt...

liebe Grüße,

hanselmansel
  Mit Zitat antworten Zitat
MathiasSimmack
 
#4
  Alt 8. Apr 2007, 08:55
Meiner Meinung nach stellt sich die Frage nicht. Wenn der Quellcode nicht vorliegt, jemand aber Funktionen nutzen will, dann liegt in irgendeiner Form eine Art Dokumentation vor. Und wenn sich der Programmierer an einen Ansatz hält, so wie Luckie ihn vorstellt, dann begreift man auch anhand der Funktionsnamen und Parameterangaben welchen Sinn das hat.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

 
Delphi 2006 Professional
 
#5
  Alt 8. Apr 2007, 09:35
Das zum einem und wenn man eine moderne IDE mit CodeInSight benutzt kann man ganz gut raten. In der VCL haben alle Enumerationstypen als Prefix die ersten beiden Buchstaben der Eigenschaft, siehe TMemo.TextAlignment -> ta... Man könnte es bei mir jetzt mit pa... probieren.
Michael
  Mit Zitat antworten Zitat
Billi Berserker
 
#6
  Alt 8. Apr 2007, 10:11
Aufzählungstypen zu benutzen kann in den meisten fällen sehr hilfreich sein.
Nur habe ich dazu auch auch eine weiterführende Frage,
Wie speichert man diese am besten?

Nehmen wir das Beispiel von oben, ich habe eine variable vom Typ TPageNumberAlignment = (paCenter, paLeft, paRight);
Den Wert möchte ich nun in eine xml Datei speichern und auch wieder laden.
Gibt es eine möglichkeit den Aufzählungstyp automatisch in einen Integer Wert (basierende auf dem Index in der List?) umzuwandeln?
So das praktisch paCenter in 0, paLeft in 1 und paRight in 2 umgewandelt wird und ich das ganze auch rückwärts machen kann.
Oder gibt es sogar eine möglichkeit das ganze in einen String mit dem Namen umzuwandeln? so das man den TPageNumberAlignment Wert in 'paCenter', 'paLeft', 'paRight' umwandelt und das ganze gut lesbar in der xml Datei speichern kann.
Das ginge natürlich wenn man sich selbst eine case anweisung schreibt... aber bei vielen und großen Aufzählungstypen wird das irgendwann ziemlich ätzend
Gibt es irgendwie eine effektive möglichkeit die case anweisung zu umgehen?
  Mit Zitat antworten Zitat
Benutzerbild von St.Pauli
St.Pauli

 
Delphi 7 Personal
 
#7
  Alt 8. Apr 2007, 10:18
Ich denke, dass bei deinem WinAPI-Beispiel mehr der Benutzer als Microsoft gefragt ist. Das Problem liegt hier weniger bei der Funktionsdeklaration, als bei der Parameterübergabe im Programm. Der Programmier muss den Funktionsaufruf so schreiben, dass er auch in 10 Jahren noch ungefähr weiß, was er damit bezwecken wollte. Mit einem simplen

Code:
hGlobalWriteEvent = CreateEvent(NULL, TRUE, TRUE, "WriteEvent");
würde er dies mit Sicherheit nicht. Am einfachsten wäre es, für jeden Parameter eine Variable zu verwenden.

Code:
LPSECURITY_ATTRIBUTES SecurityAttributes = NULL;
BOOL ResetManual = TRUE;
BOOL SignaledInitState = TRUE;
LPCTSTR EventObjectName = "WriteEvent";

hGlobalWriteEvent = CreateEvent(SecurityAttributes, ResetManual, SignaledInitState, EventObjectName);
Noch etwas umfangreicher, aber wahrscheinlich auch noch besser wäre, eine eigene Schnittstelle für die WinAPI zu schreiben, wo man die Funktionen direkt in Klassen packen könnte. Man verwendet in seinem Programm dann nur noch die gut leserlichen Schnittstellenfunktionen.
  Mit Zitat antworten Zitat
Hawkeye219

 
Delphi 2010 Professional
 
#8
  Alt 8. Apr 2007, 10:23
Hallo,

Zitat von Billi Berserker:
Gibt es irgendwie eine effektive möglichkeit die case anweisung zu umgehen?
das geht unter Verwendung der Unit TypInfo:

Delphi-Quellcode:
// uses TypInfo

type
  TPageNumberAlignment = (paCenter, paLeft, paRight);

procedure TDemoForm.Button1Click (Sender: TObject);
var
  pna : TPageNumberAlignment;
  s : string;
begin

  pna := paLeft;
  s := GetEnumName(TypeInfo(TPageNumberAlignment), Ord(pna));
  ShowMessage (s);

  s := 'paRight';
  pna := TPageNumberAlignment(GetEnumValue(TypeInfo(TPageNumberAlignment), s));
  ShowMessage (IntToStr(Ord(pna)));

end;
Gruß Hawkeye
  Mit Zitat antworten Zitat
Benutzerbild von thkerkmann
thkerkmann

 
Delphi 2010 Professional
 
#9
  Alt 8. Apr 2007, 10:24
Hi,

sicher gibt es solche Möglichkeiten.

Zunächst mal kann man mit ord(paCenter) den Ordinalwert des Aufzählungstypen bekommen, und mit einem Typecast wieder in den entsprechenden Aufzählungstypen zurückwandeln.

Mit Hilfe der Delphi Runtime Type Information kann man auch die Namen bekommen - allerdings weiss ich jetzt nicht genau wie das geht. Stichwort RTTI und siehe auch ObjectInspector.

Aber Achtung:
XML ist nicht gedacht als menschenlesbare Schreibeweise. Es ist nicht wirklich erforderlich hier aufschlussreiche Textformen zu verwenden. Das geht glaub ich ein bisschenn zu weit. XML dient dem Datenaustausch zwischen Programmen, und kann daher ruhig mit den ordinalen Werten arbeiten.

Gruss
Thomas Kerkmann
  Mit Zitat antworten Zitat
Reinhard Kern
 
#10
  Alt 8. Apr 2007, 15:38
Zitat von Luckie:
...
Kommen wir zurück zu meinem konkreten Problem. Es hat etwas gedauert, aber ich bin dann doch auf eine gute Lösung gekommen. Ich habe mich nachher etwas geärgert, dass ich so lange dafür gebraucht habe, obwohl ich sie die ganze Zeit vor der Nase hatte: Die Lösung ist eine Aufzählung. .
Hallo Luckie,

etwa 25 - 40 Jahre, das ist durchaus rekordverdächtig. Das stand schon in meinem Compilerhandbuch von 1981, aber ich vermute stark, dass Aufzählungstypen schon im Jensen-Wirth von 1970 definiert waren - den müsste ich aber erst wieder finden. Die ersten Pascalprogramme hatte ich auf der UCSD-Pascal-Engine geschrieben, so etwa 1978.

Willkommen in der neuen Welt von Pascal.

Gruss Reinhard
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:59 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz