int21h

Jak psát pěkný kód v pascalu

O dokumentu

Tento dokument popisuje vhodný styl pro formátování zdrojových kódů v Pascalu. Vychází z dokumentu Object Pascal Style Guide, který popisuje standardní styl pro formátování zdrojových kódů v Delphi.
© 2006 kdoasi

Obsah

  1. Nejdůležitější zásady
    1. Obecné zásady
    2. Identifikátory
    3. Názvy identifikátorů
    4. Odsazení
    5. Bílé znaky
  2. Zdrojové soubory
    1. Pojmenování souborů na disku
    2. Deklarace uses
    3. Konstanty a inicializované konstanty
    4. Datové typy
    5. Proměnné
    6. Procedury a funkce
    7. Příkaz if
    8. Příkaz for
    9. Příkaz while
    10. Příkaz repeat
    11. Příkaz case

1. Nejdůležitější zásady

1.1 Obecné zásady

Žádný řádek ve zdrojovém kódu by neměl být delší než 80 znaků. Na každý řádek píšeme nejvýše jeden příkaz.
  DoSomething;
  DoThis;
  DoThat;
Špatný způsob
  DoSomething; DoThis;
  DoThat;

Psaní dlouhých příkazů

V případě dlouhých příkazů a výrazů, kdy délka přesahuje 80 znaků, je vhodné řádek zalomit za příslušným binárním operátorem.
  Result := LongExpression1 or ... or LongExpressionX or
    LongExpressionY or ... LongExpressionZ;
  
  Sum :=
    VeryLongStatement1 + ... + VeryLongStatementN;
    
  Sum := VeryLongStatement1 + ... + VeryLongStatementX +
    VeryLongStatementY;    
Nevhodný způsob zalomení:
  Result := LongExpression1 or ... LongExpressionX
    or LongExpressionY or ... LongExpressionZ;

1.2 Identifikátory

U všech identifikátorů píšeme velké první písmeno. Pokud je název identifikátoru složen z více slov, první písmeno každého takového slova píšeme obvykle velké. Například
VeryLongIdentifier : String;
Takovému způsobu pojmenování se říká pascalovský styl (Pascal casing, InfixCaps). Je to klasický způsob pojmenovávání identifikátorů v Pascalu, používá se však i v jazyce C#. Ve výjimečných případech (výčtové typy) můžeme použít i jiný způsob zápisu, velbloudí styl (Camel casing, CamelCaps). Oba styly zápisu se liší pouze prvním písmenem v identifikátoru, identifikátor podle velbloudího stylu má malé první písmeno. Klíčová slova píšeme malými písmeny. Spojování slov podtržítky se nedoporučuje. Rovněž je nevhodné používat kapitálky (velká písmena), ani pro názvy konstant.
Správný zápis
  type
    TButtonKind = (bkOk, bkCancel, bkRetry); (* velbloudí styl, výjimka u výčtového typu *)
  var
    ConfigPath: String;
    AverageSpeed: Integer;
    btnClose: TButton;
Špatný zápis
  VAR
    Config_Path: string;
    averagespeed: integer;
    UGLY_VALUE: longint;

1.3 Názvy identifikátorů

Jak přehledně zapisovat názvy identifikátorů již víme, nyní si řekneme, jak jejich názvy vhodně zvolit, aby bylo jasné, k čemu identifikátor slouží, zda popisuje proměnnou nebo metodu.
První zásadní věc je nepoužívání češtiny při vytváření názvů jakýchkoliv identifikátorů. Zdrojový kód pak vypadá značně amatérsky. Češtinu lze tolerovat pouze ve výukových materiálech. Zda jsou komentáře česky nebo anglicky záleží na autorovi, v případě smysluplného kódu vystaveného na internetu je rozumnější zvolit angličtinu.
Při pojmenovávání proměnných je obecně doporučováno nepoužívat typ této proměnné v jejich názvu. Příklad nevhodného pojmenování:
SpeedInt: Integer;
Procedury, funkce a metody by měly obsahovat sloveso v rozkazovacím způsobu nebo jiný výraz se slovesem.
  procedure DoSomething(ParamX: Longint);
  function IsDeviceConnected: Boolean;

1.4 Odsazení

Vždy odsazujeme o 2 znaky. V žádném případě nepoužíváme znak tabelátor. Samozřejmě klávesu "tabelátor" použít můžeme, pokud víme, že do zdrojového kódu vkládá mezery a nikoliv znak #8.
Klíčové slovo begin se neodsazuje a je vždy samostatně na řádku. Klíčová slova unit, uses, type, interface, implementation, initialization, finalization musí být zarovnány s levým okrajem, píší se tedy vždy s nulovým odsazením.
Správné odsazení:
  if Condition = false then
  begin
    DoThis;
    DoThat;
  end else
  begin
    DoSomethingElse;
    ...
  end;
Špatné odsazení:
  if Condition = false then
    begin                    (* odsazený begin, nemá být odsazený *)
      DoThis;
      DoThat;
    end else begin           (* begin není na samostatném řádku *)
      DoSomethingElse;
      ...
    end;
    
  if Condition then          
  begin DoThis;              (* velmi špatný způsob odsazování *)
        DoThat;
  end;

1.5 Bílé znaky

Kdy se mezera nepíše: Kdy se mezera píše:
  type
    TLines = array [0..9] of String;
  var
    Lines: TLines;
  begin
    Result := ComputeSomething(ParamX, ParamY);
    Window.Show;
    DoSomethingElse(@Result);
    DoSomethingElse(@Lines[5, 20]);
  end;
Ukázka nepěkného formátování
  (* chybí mezera mezi array a levou hranatou závorkou *)
  type
    TLines = array[0..9] of String;
    
  (* nadbytečná mezera před dvojtečkou *)
  var
    Lines : TLines;
    
  (* chybějící a přebývající mezery *)
  begin
    Result:=ComputeSomething(ParamX, ParamY);
    Window. Show;
    DoSomethingElse( @ Result);
    DoSomethingElse(@ Lines [ 5 , 20 ] );
  end;

2. Zdrojové soubory

2.1 Pojmenování souborů na disku

Zdrojové soubory Turbo Pascalu, Borland Pascalu a jednotek Delphi (jakékoliv verze) mají příponu pas. U zdrojových souborů FreePascalu je vhodné použít příponu pp. Přípony se píší malým písmenem.
Názvy zdrojových souborů se pojmenovávají klasickým způsobem, tedy například
System.pas, SysUtils.pp, WinBase.pas, OpenGL.pp
Příklad špatného pojmenování:
opengl.pp, system.PAS, Win_Base.PAS, SYSUTILS.PAS

2.2 Deklarace uses

Příklad správného zápisu. Je třeba dodržet maximální délku řádku 80 znaků.
  uses Crt, Printer;
  
  uses Crt, Printer, SysUtils, Windows, WinBase,
    WinConst, Drawing, Objects;
    
  uses
    Crt, Printer, SysUtils, Windows, WinBase,
    WinConst, Drawing, Objects;

2.3 Konstanty a inicializované konstanty

Pro pojmenování konstant používáme klasický styl formátování:
  const
    Radius = 0.5;
    StartLength: Integer = 6;
    
    BUFSIZE = 1024; (* nevhodné užití velkých písmen *)
    Buf_Count = 16; (* nevhodné užití podtržítka *)

2.4 Datové typy

Řídíme se klasickou konvencí, k identifikátorům datových typů je však rozumné přidávat počáteční písmeno T. Pokud definujeme datový typ ukazatel, přidáváme počáteční písmeno P. Při definici výčtového typu je možné použít velbloudí styl. Používání mezer je probráno v kapitole o bílých znacích. Správný zápis:
  type
    TLine = array [0..255] of Char;
    TMatrix = array [0..255, 0..100] of Real;
    TWindowsStatus = (wsActive, wsInactive, wsInvisible); (* velbloudí styl *)
    
    PPerson = ^TPerson;
    TPerson = record
      Name: String;
      Age: Integer;
      Next: PPerson;
    end;
Špatný zápis:
  type
    (* chybí mezera mezi array a levou hranatou závorkou *)
    TLine = array[0..255] of Char;
    
    (* typ nezačíná písmenem T a chybí mezera za čárkou *)
    Matrix = array [0..255,0..100] of Real;
    
    (* nevhodný zápis *)
    TWindowsStatus=( WS_ACTIVE, WS_INACTIVE, WS_INVISIBLE );
    
    (* datovému typu ukazující na záznam chybí počáteční písmeno P *)
    Person = ^TPerson;
    
    (* nevhodný zápis a špatné odsazení o 3 znaky *) 
    TPerson=
    record
       Name: String;
       Age: Integer;
    end;  

Třída (datový typ class a object)

Pro definici třídy platí klasické pravidla, dále je vhodné dodržovat správné pořadí zápisu atributů, vlastností a metod a jejich rozdělení do rozsahů viditelnosti. Metody lze sestavit podle abecedního pořadí, přičemž konstruktory mohou být uvedeny jako první.
  unit BaseClasses;
  type
    TMyOldPascalClass = object(TObject)
    private
      Position: Longint;
    public
      constructor Init(Position: Longint);
      destructor Done; virtual;
    end;
    
    TMyDerivedClass = class(TMyClass)
    private
      FValue: Integer;
      procedure SetValue(Value: Integer);
    protected
      procedure Sort;
    public
      constructor Create;
    published
      property Value read FValue write SetValue;
    end;
  
  implementation
    
  constructor TMyOldPascalClass.Init(Position: Longint);
  begin
    self.Position := Position;
  end;
    
  ...
    
  end.

2.5 Proměnné

Proměnné deklarujeme klasickým způsobem. Je možné deklarovat více proměnných na jednom řádku, ale objekt je vhodné deklarovat na samostatném řádku. Nezapomeneme za dvojtečku (oddělovač proměnné a datového typu) vložit mezeru, před dvojtečku se mezera nevkládá.
  var
    SizeX, SizeY: Integer;
    MainWindow: TWindow;
    AboutWindow: TWindow;
Nevhodná deklarace:
  var
    SizeX, SizeY :Integer;             (* špatně zvolené mezery *)
    MainWindow, AboutWindow : TWindow; (* více objektů na jednom řádku *)

2.6 Procedury a funkce

Pro pěknou definici procedur a funkcí použijeme známé pravidla. Je vhodné poznamenat, že funkci definujeme pro kód vracející právě jednu hodnotu, procedury definujeme pro kód, který nevrací žádnou hodnotu nebo vrací alespoň dvě hodnoty. Nikdy bychom neměli vytvářet funkci, která vrací výsledky odkazovými parametry.
  procedure DoSomething;
  begin
    Compute;
    ...
  end;
  
  procedure FindTwoSolutions(var ResultX, ResultY: Integer);
  
  function SquareRoot(Value: Real): Real;
  
  (* zalomení dlouhé hlavičky, není zde dodržen pascalský styl formátování, protože se jedná o WinAPI funkci, tedy psanou céčkovým stylem *)
  function CreateWindowEx(dwExStyle: DWORD; 
    lpClassName: PChar; lpWindowName: PChar; 
    dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
    hWndParent: HWND; hMenu: HMENU; hInstance: HINST; 
    lpParam: Pointer): HWND; stdcall;
Příklad nevhodného zápisu
  (* funkce by měla vracet pouze jednu hodnotu! *)
  function FindTwoSolutions(var ResultX: Integer): Integer;
  
  (* nesprávné a nejasné zalomení hlavičky *)
  function CreateWindowEx(dwExStyle: DWORD; 
    lpClassName: PChar; lpWindowName:
    PChar; dwStyle: DWORD; X, Y, nWidth, nHeight:
    Integer; hWndParent: HWND; hMenu: HMENU; hInstance:
    HINST; lpParam: Pointer): HWND; stdcall;

2.7 Příkaz if

Při použití tohoto příkazu si stačí uvědomit, že klíčové slovo begin bychom měli psát na zvláštní řádek. Je také doporučeno psát příkaz celou konstrukci příkazu if minimálně na dva řádky. V případě složitých podmínek se logické binární operátory (and, or, xor) píší na konec řádku.
  if A < B then
    DoSomething;
    
  if A < B then
  begin
    DoSomething; 
    DoSomethingElse;
  end else
    DoThis;    
    
  if A < B then
  begin
    DoSomething; 
    DoSomethingElse;
  end else
  begin
    DoThis;
    DoThat;
  end;
  
  if A < B then
  begin
    DoSomething; 
    DoSomethingElse;
  end
  else
    DoThis;
  
  if A < B then 
  begin
    DoSomething; 
    DoSomethingElse;
  end 
  else 
  begin
    DoThis;
    DoThat;
  end;
  
  (* or je správně na konci řádku *)
  if (LongExpression1) or 
    (LongExpression2) or 
    (LongExpression3) then
  begin
    ...
  end; 

Nevhodný zápis:
  if A < B then DoSomething;
  
  if A < B then
    DoSomething
  else DoThis;
  
  if A < B then begin
    DoSomething; 
    DoSomethingElse;
  end else begin
    DoThis;
    DoThat;
  end;
  
  (* or je nevhodně na začátku řádku *)
  if (LongExpression1) 
    or (LongExpression2) 
    or (LongExpression3) then
  begin
    ...
  end;

2.8 Příkaz for

Příklad správně zapsaného cyklu:
  for i := 0 to 10 do 
  begin
    DoSomething; 
    DoSomethingElse;
  end;
Příklad nesprávně zapsaného cyklu:
  for i := 0 to 10 do begin
    DoSomething; 
    DoSomethingElse;
  end;

2.9 Příkaz while

Příklad správně zapsaného cyklu:
  while x < j do 
  begin
    DoSomething; 
    DoSomethingElse;
  end;
Příklad nesprávně zapsaného cyklu:
  while x < j do begin
    DoSomething; 
    DoSomethingElse;
  end;

2.10 Příkaz repeat

Příklad správně zapsaného cyklu:
  repeat
    x := j;
    j := UpdateValue;
  until j > 25;

2.11 Příkaz case

U příkazu větvení si můžeme všimnout výjimky ve způsobu odsazení bloků begin end kvůli zvýšení přehlednosti. Příklady doporučeného způsobu zápisu:
  case Control.Align of
    alLeft, alNone: NewRange := Max(NewRange, Position);
    alRight: Inc(AlignMargin, Control.Width);
  end;


  case x of

    csStart:
      begin
        j := UpdateValue;
      end;

    csBegin: x := j;

    csTimeOut:
      begin
        j := x;
        x := UpdateValue;
      end;
      
  end;
      

  case ScrollCode of
    SB_LINEUP, SB_LINEDOWN:
      begin
        Incr := FIncrement div FLineDiv;
        FinalIncr := FIncrement mod FLineDiv;
        Count := FLineDiv;
      end;
    SB_PAGEUP, SB_PAGEDOWN:
      begin
        Incr := FPageIncrement;
        FinalIncr := Incr mod FPageDiv;
        Incr := Incr div FPageDiv;
        Count := FPageDiv;
      end;
  else
    Count := 0;
    Incr := 0;
    FinalIncr := 0;
  end;
2006-11-30 | kdoasi
Datum: 8.3.2008 14:55
Od: Mircosoft
Titulek: alternativně anarchistický názor
Souhlas:
Nemám rád tabulátory (#9) ve zdrojáku.

Nesouhlas:
Nemám rád zbytečné mezery (okolo :=, = apod.).
Nemám rád zbytečné odsazování za každou cenu.
Nemám rád, když příkazy mezi begin a end nejsou zarovnané stejně jako ten begin a end.
Nerad píšu velkými písmeny, pokud to není nezbytně nutné.
Rád píšu víc příkazů na jeden řádek, pokud jsou krátké, souvisí spolu a nezhorší se tím přehlednost (begin x:=3; y:=5; end).
Rád odlišuju klíčová slova velikostí písma podle důležitosti (BEGIN na začátku programu, Begin na začátku procedury a všude jinde jenom begin).

Žádný "jediný správný styl, všechny ostatní jsou špatné" neexistuje. Jsou jenom styly přehlednější a méně přehledné.
Reklamy: