![]() |
Codedesign
Ich bekam jetzt mal Code zusehen, der meiner Meinung vom Design her schelcht war. Daraufhin habe ich ihn überarbeitet - eigentlich total omgekrempelt. Dabei habe ich mir einige Gedanken gemacht, was guten Code von schlechten unterscheidet. Als Essenz bin ich dabei auf folgende Regeln gekommen:
Anmerkunmg zum letzten Punkt: Diesen habe ich noch wie folgt erläuteret: Zitat:
|
Re: Codedesign
Grundsätzlich stimm ich den Punkten mal zu. Zum Thema subroutinen äußer ich mich lieber nich da ich sowas verabscheue, da sieht kein schwein mehr durch und in den meisten fällen würde es auch nicht schaden diese einfach in eine eigene funktion zu packen und diese dann in den privateteil zu packen.
Ich selbst programmier mehr nach dem Try and Error Prinzip und wenns dann bissl mehr source wird, wird umsortiert und zwischen den funktionen eine zeile {===} eingefügt damit man beim durchscrollen durch die unit schneller funktionsanfang und -ende findet... aber wer hier schonmal bissl source von mir runtergeladen hat der wird da in den seltensten fällen durchgesehen haben und wenns nach meinem scheff geht muss ich mich och mal dazu durchringen in der Zukunft kommentare im Source unterzubringen aber bins halt gewöhnt source von anderen durchzuwühlen um dann zu wissen wie was funktioniert... So sind die Punkte eigentlich ne supi Grundlage für Open-Sourceprogrammierung und Projekte wo mehrere Leutz mitmachen |
Re: Codedesign
Zitat:
|
Re: Codedesign
Achso, dachte mit Subroutinen waren die Proceduren/Funktionen gemeint die innerhalb einer Funktion definiert sind
|
Re: Codedesign
Zitat:
|
Re: Codedesign
ok, dann will ich mich halt zu den inlay-Prozeduren/Funktionen nicht weiter äußern (thx für die Theorie, war schon kurz davor nen thread dazu aufzumachen)
Der Punkt das "normale" proceduren und Funktionen nicht länger als eine Bildschirmseite sein sollten erscheint mir bissl unmöglich. Bei gewissen Projekten ist das einfach nicht möglich. Bsp.: 2D/3D-Move berechnungen. Außerdem kann man ja die übersichtlichkeit da waren in dem man wie schon erwähnt zwischen die Proceduren/Funktionen "{=======}" einfügt |
Re: Codedesign
Hmmm. Jetzt wo ich das einmal runtergeschrieben habe, sind doch ein paar Fragen zum Design "dazwischengerutscht"... Trotzdem ist vieles zum Theme "Code" dabei. Was haltet ihr von dieser Ergänzung?
Klarer Entwurf
Nutzung der Typenstrenge in Pascal/Delphi Language
Kapselung, Geheimnisprinzip
Vorbedingungen und Nachbedingungen
Testen, Debugging, Profiling
Es ist nichts schlimmes, die Lösung in mehreren Iterationen zu verbessern. Auch hier ist die Hilfe dritter unbezahlbar! |
Re: Codedesign
Irgendwo habe ich mal gelesen das Sakura zitiert hat, dass Prozeduren mit cirka 70 Zeilen länge am wenigsten Fehler enthalten, deshalb versuche ich persönlich zusatzfunktionen nicht umfangreich zu gestalten und wenn auf mehrere Funktionen aufzuteilen.
Ansonsten empfehle ich den OP Style Guide (gibts bei Luckie auf der Page :mrgreen: ). Was ich ansonsten überhaupt nicht abkann (was man hier auch manchmal im Forum sieht) sind PERMANENTE GROSSSCHREIBUNG, so Spaghetticode, und nicht schön abgestufter Code :love: . Naja das wars von meiner Seite zu dem Thema ;) mfg phlux :hi: |
Re: Codedesign
Zitat:
|
Re: Codedesign
und das diese Inlay funktionen offiziell "nested" Funktionen heissen :)
Ich persönlich sehe nested Funktionen als guten OOP Stil an. Sie sind nämlich meistens nur 3-4 Zeilen lang und fassen Funktionalität zusammen die mehrmalig im übergeordneten Code benötigt wird, und zwar ausschließlich NUR im übergeordneten Code. Sobald diese Funktionalität interessant für andere Funktionen wird sollte man sie auslagern. Nested Funktion sind deshalb auch übersichtlicher ! Denn man wird sie einmalig lesen und verstehen, und konzentiert sich dann auf den eigentlichen Code der natürlich nun viel kürzer und besser lesbar ist ! Wird nun die eigentliche Funktion kopiert, weil man sie eventuell wo anders einmalig benötigt, so kopiert man immer auch die neseted Funktionen mit. Somit verliert man nicht eventuell kurze Codestücken, oder muß diese aus anderen Units zusammen suchen. Sie pauschal zu verdammen oder immer als gut zu heissen ist sehr schlechter "OOP/NOP" Stil ! Gruß Hagen |
Re: Codedesign
Nested functions haben auch noch weitere Vorteile.
Erstens hat man eine Teilfunktionalitaet isoliert, die man bestimmt bald als lokale Funktion brauchen kann. Ganz anders ist der Vorteil das man Zugriff auf uebergeordnete Variablen hat. Das muss man allerdings sorgfaeltig evaluieren, da es sehr unuebersichtlich werden kann. |
Re: Codedesign
Hallo choose,
wäre vielleicht mal ganz nett, wenn du deinen Beitrag ins Deutsch übersetzen könntest, ich als Hochschulabgänger und Softwareentwickler versteh ihn nämlich nicht! Nimoee |
Re: Codedesign
Hallo Nimoee,
ich bin den Text noch einmal durchgegangen, Du hast Recht: Obwohl ich mir Mühe gegeben habe, sind in der Tat noch einige englische Begriffe im Text vorhanden... Mit den folgenden Begriffen meinte ich das:
Ich hoffe, dass ich Dir damit helfen konnte. Wie ich im letzten Posting schon erwähnte, es sind eher Entwurfsentscheidungen (und ein bisschen Vorgehensmodell), die ich hier aufgezählt habe. Trotzdem beeinflussen einige von ihnen stark die lesbarkeit des Codes. |
Re: Codedesign
Zurück zum Thema, ihr meint also, dass ich die wichtigesten Regeln gefunden habe oder könnte man noch was ergänzen? Es soll eigentlich ein kleiner Artikel für Einsteiger werden. Und so tief wie choose wollte ich nicht in die Materie einsteigen, damit dürften Anfänger überfordert sein.
|
Re: Codedesign
Naja, eines noch: man sollte wissen wie was wann der Compiler compiliert :-)
D.h. man sollte wissen was die einzelnen Aufruf Konventionen für direkte und indirekte Auswirkungen auf das beste Codedesign haben. Zb. arbeiten wir standardmäßig mit Register Aufrufkonvention. Beachtet man im Design das jede Funktion max. 3 Parameter ordinalen Types hat -> Integer/Pointer und Klassen Methoden max. 2 solcher Parameter, dann ist dies die optimale Vorgehensweise damit der Optimierer schnellen Code erzeugen kann. Man sollte dann auch bedenken das die Anzahl der lokalen Variablen in den Proceduren gering gehalten wird. Auch dies ermöglicht dem Optimierer die bestmögliche Arbeitsweise um mit den vorhandenen Registern jonglieren zu können. Auch deshalb sind nested Proceduren sinnvoll. Denn der Optimeirer optimiert jede Funktion für sich gesehen. Je einfacher und linear strukturiert eine Procedure ist je besser kann der Optimierer seine Arbeit machen. Es gibt also direkte Abhänigkeiten des erzeugten Codes durch den Compiler zum gewählten Design. In kurz: - 3 ordinale Paramter maximal für Proceduren - 2 ordinale Paramter maximal für Methoden (Self ist der 3. unsichtbare Parameter) - register Aufrufkonvention wenn möglich - Result immer nur am Ende der Funktion belegen, oder vor einem Exit; - möglichst wenige lokale unkomplizierte Variablen - das Aufsplitten komplexer Funktionen auf mehrere weniger komplexe Funktion macht den Code meistens schneller, da der Optimierer besser arbeiten kann (gilt auch für nested Proceduren) Mit dem Result hier ein Source:
Delphi-Quellcode:
In komplexeren Sources wird nämlich in BadResultUsage das Resultat im Stack zwischen gespeichert. In GoodUsage geben wir aber dem Optimierer in J die Berechnungsvariable vor, die der Optimierer innerhalb der Loop in ein Register optimieren kann. Erst am Ende der Funktion wird das Resultat belegt, was meistens durch den Optimierer einfach bedeutet das er das optimierte Register J ind Register EAX kopiert. In BadResultUsage wäre dies aber im Stack gespeichert.
function BadResultUsage: Integer;
var I: Integer; begin Result := 1; for I := 0 to 1024 do Result := Result + Result; end; function GoodUsage: Integer; var I,J: Integer; begin J := 1; for I := 0 to 1024 do J := J + J; Result := J; end; Wohlgemerkt obige Beispiele sind zu einfach, das heist der Optimierer kann so wie sie sind beide optimal umsetzen. Würde man aber viel mehr Code drinnenstehen haben tritt diese Regel in Kraft. Nochwas: man sollte nach Möglichkeit mit Funktionen mit ordinalen Rückgabewerten arbeiten. Proceduren mit Reückgabeparametern bei Referenzen sind ineffizienter.
Delphi-Quellcode:
Gruß hagen
procedure BadResultProc(var Result: Integer; Data: Integer);
begin Result := Result + Data; end; function GoodResultFunc(PrevResult, Data: Integer): Integer; begin Result := PrevResult + Data; end; |
Re: Codedesign
Gute Tipps, wußte ich auch noch nicht. Aber ich weiß nicht, ob ich das noch einbaue, da der Artikel nicht compilerabhängig sein sollte. Mal sehen eventuell als anhang oder so. hast du dazu noch irgendwelche quellen im Internet oder so?
|
Re: Codedesign
Bei Prozedure/Funktionen/Methoden die mit Interface/Floatingpoints/Variant/Strings Paramter arbeiten sollte man const Paramter benutzen.
const deklarierte Paramter verhindern das der Compiler das nötige Referenzecounting bei diesen komplexen Typen durchführt.
Delphi-Quellcode:
Im zweiten Falle weiß der Compiler durch die const Deklaration das sich nichts and den Daten ändern wird, es geht auch garnicht. Dadurch kann er sich die Einrichtung eines unsichtbaren try finally end Blockes und den Code für's Referenzecounting vollständig sparen.
procedure BadRefCountedTypes(Data: String/Variant/Interface);
begin // _AddRef(Data); // intern durch Compiler erzeugt // try if Data <> ... then ; // finally // _Release(Data) end; procedure GoodRefCountedTypes(const Data: String/Variant/Interface); begin if Data <> ... then ; end; Bei Interfaces zB. sind Parameter die nicht const deklariert wurden fast immer schlecht deklariert, da man sie eigentlich immer nur als const oder var benutzen kann. Gruß Hagen |
Re: Codedesign
Zitat:
Selbst in den Borland NewsGroups existieren darüber falsche Ansichten, auch bei TeamB Mitgliedern. Gruß Hagen PS: man kann sich die logischen Abhänigkeiten aus der Delphi Hilfe ableiten, mit ein wenig Geschick :) |
Re: Codedesign
Hm, gut. Mal sehen, ob ich die gepordnet da unterbringen kann. Jetzt koche ich mir aber ertsmnal meine Eier.
|
Re: Codedesign
Zitat:
Wo genau soll denn der Schwerpunkt Deines Artikels liegen? Deine bisherigen Vorschläge scheinen sich auf Wartbarkeit zu beziehen. Hagen schein die Erweiterbarkeit im Sinn zu haben Zitat:
Was aber willst Du in Deinem Artikel darstellen? |
Re: Codedesign
Liste der Anhänge anzeigen (Anzahl: 1)
ja Moment. Ich hänge ihn mal an.
|
Re: Codedesign
Zitat:
Wichtig ist das das Vorurteil das ein Source subjektiv und Geschacksache ist aus der Welt zu schaffen. Denn Programmierung ist reinste Logik und da haben Gefühle wie Geschmack nichts zu suchen. Man kann also ein gutes Code Design absolut logisch und schlüssig erklären warum es so am besten ist. Eine der wichtigsten Regel ist es: Der Programmierer codiert NICHT aus Selbstzweck und schon garnicht so das er sich in seinem Stil von allen anderen Programmierern unterscheidet !! Dies ist dumm und arrogant.
Delphi-Quellcode:
Ist absolute Scheiße !
PROCEDURE _Tue_was_schlechter_stIEL(var
param_1: XType); BEGIN IF BooleanVariable = TRUE THEN BEGIN Display; END END 1.) Underline als Separator ist C/C++ Stil 2.) Underline am Anfang definiert virtuell einen Compiler Magic 3.) Compiler Tokens werden immer kleine geschrieben 4.) Blöcke werden niemals auseinander gerissen, wie beim obigen IF THEN 5.) begin end/try finally end/try except end definieren einen Block Anfang und End deshalb gehören sie auf selbe Einrückungsebene 6.) immer 2 Leerzeichen werden Blöcke eingerückt, nicht 3,4,5 oder 8 und schon garnicht Tabulatoren !! 2 Leerzeichen nichts anderes. Es kommt häufig vor das man einem PASCAL Source in anderen Editoren betrachten und Tabulatoren zerstören die Formatierungen. 7.) Hinter JEDEM end gehört ein Semikolon, auch wenn es nicht unbedingt notwendig ist 8.) if then erwartet eine Boolsche Abfrage, einen Boolean nochmals mit einer Boolschen Abfrage zu versehen deutet darauf hin das der Programmierer nicht rechnen kann und die Boolsche Algebra in der Schule verpennt hat. Gruß Hagen |
Re: Codedesign
Prima. :P Unterhaltet euch nur schön weiter so, ich mache mir Notizen. :wink: Der Artikel oben ist übrigens erst mal nur ein grober Entwurf. Da muss auch sprachlich noch gefeilt werden, damit er sich flüssig liest.
@Hagen: Das geht aber schon in Richtung Codeformatierung, darüber wollte ich mich weniger auslassen. Dazu gibt es ja schon den Style Guide von Calvert. |
Re: Codedesign
Ein gutes Beispiel für relativ schlechten Code findet man in der INDY Library.
Ein gutes Beispiel für relativ guten Code findet man in der JEDI Library. Gruß Hagen |
Re: Codedesign
Hallo Luckie,
ich habe Deinen Text einmal überflogen und finde ihn ziemlich gut aufgemacht. Ähnlichen Code konnte ich bei vielen Einsteigern, die sich den Problemen nicht bewusst waren, ebenfalls entdecken. Du zeigst recht pragmatisch, wovon andere Autoren in abstrakten Texten mit Realitätsfremden Diagrammen berichtet wird (bitte nicht missverstehen, ich bin ein Fan des MVC-Paradigmas!). Hauptaugenmerk richtest Du nach meinem Empfinden auf Lesbarkeit, Verständnis und Änderung des Codes. Was hältst Du von einem Folgeartikel, der Konzepte der Wiederverwendbarkeit aufzeigt? Ich könnte mir zB vorstellen, dass eine Person, die ähnliche Problemstellungen zu lösen versucht, Schwierigkeiten mit der Pflege und Übersicht den vielen ähnlichen Validierungsfunktionen begekommt... |
Re: Codedesign
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Codedesign
Nun Klammersetzung. Es sollten niemals zu viele Klammern sein, sondern nur so viele wie nötig
Delphi-Quellcode:
D.h. Boolsche Algebra heist das man mit den Operatoren AND,OR,NOT,XOR rechnen kann.if ((Boolean) and (not (Value = 1))) then Bad; if Boolean and (Value <> 1) then Good; if (A = 0) and (B = 0) and (C = 0) then Bad; if A or B or C = 0 then Good; if (A <> 0) or (B <> 0) or (C <> 0) then Bad; if A or B or C <> 0 then Good; // auch wenn dieser Code ineffizienter ist ! if ((Red in Set) or (Green in Set) or (Blue in Set) or (Set = [])) and not (Yellow in Set) then Bad; if Set * [Red, Green, Blue, Yellow] <> [Yellow] then Good; D.h. das man mit Mengen rechnen kann wie mit Boolscher Algebra. Gruß Hagen |
Re: Codedesign
:cry:
Hai Hagen, mache nur so weiter und zerstöre meine Illusion ist würde "gut" Coden ;-) Ne, im Ernst: Wenn ich das hier mitlese wird mir bewusst auf was ich alles nicht geachtet habe. Finde es allso Super! :thuimb: Gerade die kurzen Beispiele zeigen sehr gut die Unterschiede zwichen gut und schlecht. @Luckie: Auch das formatieren des Codes (inkl. der Namesvergabe von Methoden und Variablen) sollte nicht unerwähnt bleiben. |
Re: Codedesign
Zitat:
|
Re: Codedesign
@Luckie:
In deinem Tut: 1.) die ShowMessage() sollte durch eine Excpetion ersetzt werden. Der Code als "Highlevel" Funktion sollte auch eine Fehlerüberprüfung haben. "Highlevel" Funktionen können demzufolge auch Exceptions auslösen. Paralell dazu könnte man die Funktion so umbasteln wie es in StrToIntDef() der Fall ist. Dies würde die Exceptions beseitigen und einen Default Wert zuweisen. 2.) ValidatDay() ist eine Schlampigkeit, denn das eine fehlende "e" für ValidateDay() hätte man auch noch schreiben können :) Es sei denn man würde IsValidDay() benutzen. 3.) Button1.Enabled := IsValidDay and IsValidMonth and IsvalidYear; stellt klip und klar eine Boolsche Berechnung dar. Man benötigt keinerlei if then else ! Selbiges in ValidatDay() 4.) ALLE Varibalen beginnen mit Großbuchstaben ! 5.) ValidatDay(d: string): Boolean; ist inkonsitent -> IsValidDay(const Value: String): Boolean; 6.) edtDay.Text -> EditDay.Text oder EDay.Text 7.) Extended für die Berechnungen ist überdimensioniert, Default Floatingpoint sollte Double sein. 8.) CalcJulYear(Day, Month, Year: DWord): Extended -> CalcJulianYear(Year: Word; Month: Word = 1; Day: Word = 1): Word; DWord ist ein Windows API Datentyp kein PASCAL Datentyp. In PASCAL wäre es Cardinal. Gruß Hagen |
Re: Codedesign
Zitat:
ich empfehle Einsteigern meißt eher das Gegenteil, weil vielen die Prioritätsregeln nicht "im Blut stecken". Es gibt zwei Prioritätsregeln:
Delphi-Quellcode:
Und "Tricks mit binären Operatoren" in der Form
var
a, b: Integer; begin a:= -1; b:= 2; if not a < b then Zitat:
Ich hatte mal den folgenden Code gesehen:
Delphi-Quellcode:
Sicher, er funktioniert, ist performat, braucht eine Variable weniger, aber: Wer, der nicht vorher ASM geschrieben hat, versteht ihn (und dann noch der Kommentar! Ich beziehe mich hier auf Negah: "Der Programmierer codiert NICHT aus Selbstzweck")?
//its not a sony, its a trick
a:= a xor b; b:= a xor b; a:= a xor b; |
Re: Codedesign
Schon verbessert. :thumb:
|
Re: Codedesign
Delphi-Quellcode:
Gruß hagen
function IsValidDay(const Value: String): Boolean;
begin Result := StrToIntDef(Value, 0) in [1..31]; end; function CalcJulianYear(Year: Word; Month: Word = 1; Day: Word = 1): Word; begin // falls CalcJulianYear eine "lowlevel" private Funktion ist Assert(Month in [1..12]); Assert(Day in [1..31]); // falls CalcJulianYear eine "highlevel" globale Funktion ist if not (Month in [1..12]) then raise EConvertError.Create(); if not (Day in [1..31]) then raise EConvertError.Create(); // hier die Berechnung aus der PDF, ich verstehe aber nicht was dort berechnet wird, // da die wichtigen Remarks fehlen !!?? :-) end; |
Re: Codedesign
Hallo negaH,
Luckie hat bewusst darauf hingewiesen, dass der Article nicht in der finalen Version vorliegt. Tippfehler, wie ein fehlender Buchstabe, sollten deshalb verziehen werden... Zitat:
Zitat:
Zitat:
|
Re: Codedesign
Zitat:
Jede Ausnahmeregel und Ausnahmevereinfachung führt dazu das man es so und nicht anders in Zukunft macht. Einfach mal sagen das der Himmel Grün und die Wiese Blau ist, damit es ein Idiot versteht halte ich für falsch (überspitzt ausgedrückt :-) ) Dem Lernenden ist nicht geholfen dabei.
Delphi-Quellcode:
Diese Abfrage muß logisch begriffen werden, man muß also Klamern setzen wenn man den Boolschen Vergleich if not (A < B) then erreichen will.
var
a, b: Integer; begin a:= -1; b:= 2; if not a < b then Wichtig ist es hier aber das man merkt das diese Abfrage absolut schlechter Stil ist und der Programmierer NICHT nachgedacht hat. Denn:
Delphi-Quellcode:
wäre die RICHTIGE Antwort !
if a >= b then ;
Delphi-Quellcode:
Das sollte absolut vermieden werden, korrekt !
a:= a xor b;
b:= a xor b; a:= a xor b; Eine zusätzliche Variable macht den Code lesbarer und ist unter umständen schneller. Gruß Hagen |
Re: Codedesign
So, das werde ich nachher mal alles zusammentragen und mit einfließen lassen.
|
Re: Codedesign
Cool. Postest Du hier noch einmal, wenn Du den Artikel fertig und auf Deiner Seite veröffentlicht hast?
Danke. |
Re: Codedesign
Logisch, sonst macht es ja wenig Sinn. Ich muss mich nur irgendwie mit dem Autor für den schlechten Code einigen. Bis jetzt sperrt er sich noch. :cry:
|
Re: Codedesign
Zitat:
Damit kann man komplizierte abfragen vereinfachen! Man sollte aber einen Kommentar mit der Problembeschreibung dazusetzen. Was bei mir noch immer aufgefallen ist, Ich schreibe eine Procedure zuerst logisch auf. Was soll grob getan werden. ==> Kommentare Bsp: TSR
Delphi-Quellcode:
Das Symikolon hatte ich vor sehr langer Zeit hinter das do gesetzt und manchmal vergessen es wieder wegzulöschen oder man setzt einen leeren Block.
// Sortiere Feld
// Gehe Alle Städte -> Rekursion for I := 0 to Count - 1 do // Schranke prüfen ; Diese Vorgehensweise macht es mir leicht möglich das komplexen Problem in viele Teilprobleme zu zerlegen. Nennt man glaube ich Top-Down-Prinzip. |
Re: Codedesign
Gibt es nicht von Borland selbst einen seitenlangen Artikel mit Richtlinien, wie man Pascal Code schreiben sollte?
Ich bin mir sicher ich hab sowas schonmal gelesen, vielleicht weiß von euch ja einer wo es den gibt. Ich werd ihn mal suchen, den könnte man wenigstens noch als Vergleichsquelle benutzen, wenn man sich nicht komplett an ihn hält. Denn neben den Regeln die es schon gibt noch eigene Regeln aufzustellen die denen ähnlich sind die es schon gibt... Da kann ich mir auch ne eigene StVO entwerfen, die sich von der normalen nur dadurch unterscheidet dass ich immer Vorfahrt habe ;-) Nichts für ungut :-) Ich finde eure Ansätze bislang richtig gut, die machen Sinn und umfassen schon viele "Bereiche". Wenn sich daran hier im Forum jeder hält, haben wir alle mehr Spaß. Bis dann, S - tefano |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz