Spustit prezentaci

Doporučené postupy v programování

Formátování a styl psaní kódu

Základní principy · Formátovací pomůcky · Formátování bloků a elementů kódu

Lubomír Bulej

KDSS MFF UK

Základní principy

Proč je formátování a styl důležité?

Počítači je to přeci úplně jedno...

/* Use the insertion sort technique to sort the "data" array in
ascending order. This ro utine assumes that data[ firstElement ] is
not the first element in data and that data[ firstElement-1 ] can be
accessed. */ public void InsertionSort( int[] data, int firstElement,
int lastElement ) { /* Replace element at lower boundary with an
element guaranteed to be first in a sorted list. */ int lowerBoundary
= data[ firstElement-1 ]; data[ firstElement-1 ] = SORT_MIN; /* The
elements in positions firstElement through sortBoundary-1 are always
sorted. In each pass through the loop, sortBoundary is increased, and
the element at the position of the new sortBoundary probably isn't in
its sorted place in the array, so it's inserted into the proper place
somewhere between firstElement and sortBoundary. */ for ( int
sortBoundary = firstElement+1; sortBoundary <= lastElement;
sortBoundary++ ) { int insertVal = data[ sortBoundary ]; int insertPos
= sortBoundary; while ( insertVal < data[ insertPos-1 ] ) { data[
insertPos ] = data[ insertPos-1 ]; insertPos = insertPos-1; } data[
insertPos ] = insertVal; } /* Replace original lower-boundary element
*/ data[ firstElement-1 ] = lowerBoundary; }
Uvedený kód je syntakticky správně, je dostatečně okomentován, používá rozumné názvy proměnných a logika je dostatečně jasná. Přesto mu však něco chybí...

Proč je formátování a styl důležité?

Počítači je to přeci úplně jedno...

/* Use the insertion sort technique to sort the "data" array in ascending order.
This routine assumes that data[ firstElement ] is not the first element in data
and that data[ firstElement-1 ] can be accessed. */
public void InsertionSort( int[] data, int firstElement, int lastElement ) {
/* Replace element at lower boundary with an element guaranteed to be first in a
sorted list. */
int lowerBoundary = data[ firstElement-1 ];
data[ firstElement-1 ] = SORT_MIN;
/* The elements in positions firstElement through sortBoundary-1 are always
sorted. In each pass through the loop, sortBoundary is increased, and the element
at the position of the new sortBoundary probably isn't in its sorted place in the
array, so it's inserted into the proper place somewhere between firstElement
and sortBoundary. */
for (int sortBoundary = firstElement+1; sortBoundary <= lastElement; sortBoundary++) {
int insertVal = data[ sortBoundary ];
int insertPos = sortBoundary;
while ( insertVal < data[ insertPos-1 ] ) {
data[ insertPos ] = data[ insertPos-1 ];
insertPos = insertPos-1;
}
data[ insertPos ] = insertVal;
}
/* Replace original lower-boundary element */
data[ firstElement-1 ] = lowerBoundary;
}       

Proč je formátování a styl důležité?

Počítači je to jedno, ale kód čtou hlavně lidé...

/**
 * Use the insertion sort technique to sort the "data" array in ascending
 * order. This routine assumes that data[ firstElement ] is not the
 * first element in data and that data[ firstElement-1 ] can be accessed.
 */
public void InsertionSort( int[] data, int firstElement, int lastElement ) {
  //
  // Replace element at lower boundary with an element guaranteed to be
  // first in a sorted list.
  //
  int lowerBoundary = data[ firstElement-1 ];
  data[ firstElement-1 ] = SORT_MIN;

  //
  // The elements in positions firstElement through sortBoundary-1 are always
  // sorted. In each pass through the loop, sortBoundary is increased, and the
  // element at the position of the new sortBoundary probably isn't in its
  // sorted place in the array, so it's inserted into the proper place
  // somewhere between firstElement and sortBoundary.
  //
  for ( int sortBoundary = firstElement + 1; sortBoundary <= lastElement; sortBoundary++ ) {
    int insertVal = data[ sortBoundary ];
    int insertPos = sortBoundary;

    while ( insertVal < data[ insertPos - 1 ] ) {
      data[ insertPos ] = data[ insertPos - 1 ];
      insertPos = insertPos - 1;
    }

    data[ insertPos ] = insertVal;
  }

  // Replace original lower-boundary element
  data[ firstElement - 1 ] = lowerBoundary;
}       

Hlavní zásada formátování

Dobré vizuální uspořádání kódu zdůrazňuje logickou strukturu programu.


Preferujte techniky, které lépe ukazují strukturu kódu

Lidská vs. strojová interpretace kódu

Rozdíly ve vnímání programu: odsazení

// swap left and right elements for whole array
for (i = 0; i < MAX_ELEMENTS; i++)
  leftElement = left [i];
  left [i]    = right [i];
  right [i]   = leftElement;

Rozdíly ve vnímání programu: mezery

x = 3+4 * 2+7;

Radosti a starosti formátování

Psychologický efekt dobrého formátování

Náboženské války – co je dobré formátování?

Cíle dobrého formátování – shrnutí

Přesná reprezentace logické struktury kódu

Konzistentní reprezentace logické struktury kódu

Zlepšení čitelnosti kódu

Robustnost vůči modifikaci

Formátovací pomůcky

Mezery – v širším smyslu

Odsazení

Mezery

Prázdné řádky

Seskupování

Vizuální oddělovače

Komentářové bloky bez textu

// --------------------------------------------------------------------

// ********************************************************************

///////////////////////////////////////////////////////////////////////

//      

Komentářové bloky s textem – cedule

/* *******************************************************************
 * INTERFACE METHODS: DataSource
 * *******************************************************************/

Na co dávat pozor?

Závorky

Objasnění výrazů s více než dvěma termy

Jak se vyhodnotí následující výrazy?

Ne že by se vyhodnocení uvedených výrazů nedalo vymyslet, ale pointa tkví v tom, že pokud nad tím musíte i jen trošičku přemýšlet nebo si nejste bez referenční příručky jisti, je rozumné se podobné nejistotě vyhýbat na sto honů. Při použití závorek je jasně dokumentován záměr a není třeba řešit, zda to autor myslel tak jak to napsal nebo jestli se spletl v pořadí vyhodnocování operátorů.

Formátování bloků

Formátovací styly

Implicitní bloky

Abstrakce stylu

A                  
B            
C            
D      

Implicitní bloky

Příklad if bloku

If pixelColor = Color_Red Then
  statement1
  statement2
  ...
End If    

Příklad while bloku

While pixelColor = Color_Red
  statement1
  statement2
  ...
Wend      

Příklad switch bloku

Select Case pixelColor
  Case Color_Red
    statement1
    statement2
    ...

  Case Color_Green
    statement1
    statement2
    ...

  Case Else
    statement1
    statement2
    ...
End Select

Formátovací styly

Emulace implicitních bloků

Abstrakce stylu

A                 {
B            
C            
D  }   

Emulace implicitních bloků

Příklad if bloku

if (pixelColor == Color.RED) {
  statement1
  statement2
  ...
}         

Příklad while bloku

while (pixelColor == Color.RED) {
  statement1
  statement2
  ...
}         

Příklad switch bloku

switch (pixelColor) {
  case Color.RED:
    statement1
    statement2
    ...
    break;

  case Color.GREEN:
    statement1
    statement2
    ...
    break;

  default:
    statement1
    statement2
    ...
    break;
}         

Formátovací styly

Ohraničení hranic bloků

Abstrakce stylu

A                  
     {   
B            
C            
     }   

Ohraničení hranic bloků

Příklad if bloku

if (pixelColor == Color.RED)
  {
  statement1
  statement2
  ...
  }       

Příklad while bloku

while (pixelColor == Color.RED)
  {
  statement1
  statement2
  ...
  }       

Příklad switch bloku

switch (pixelColor)
  {
  case Color.RED:
    statement1
    statement2
    ...
    break;

  case Color.GREEN:
    statement1
    statement2
    ...
    break;

  default:
    statement1
    statement2
    ...
    break;
  }       

Poznámky k ohraničení hranic bloků

Vyhněte se neodsazeným begin/end

for (int i = 0; i < MAX_LINES; i++)
{
  ReadLine (i);
  ProcessLine (i);
}         

Abstrakce stylu

A                  
B       
C            
D            
E       


Vyhněte dvojitému odsazení

for (int i = 0; i < MAX_LINES; i++)
  {
    ReadLine (i);
    ProcessLine (i);
  }       

Abstrakce stylu

A                  
B         
C              
D              
E         

Přestože je první způsob poměrně často používaný, porušuje hlavní zásadu formátování – neukazuje totiž logickou strukturu kódu. Při použití tímto způsobem není begin/end součástí řídící struktury, ale ani podřízených příkazů, které následují.

Druhý způsob formátování je o něco méně častější, ale stejně jako předchozí způsob porušuje hlavní zásadu tím, že přesně neodráží logickou strukturu programu. Volání funkcí budí dojem, že jsou podřízené begin/end, což nejsou. Celkově tedy tento způsob formátování zveličuje složitost konstrukce.

Formátovací styly

Zarovnání ke konci řádku

Abstrakce stylu


A                           
B                   
C                   
D               

Zarovnání ke konci řádku

Příklad while bloku

While pixelColor = Color_Red
      statement1
      statement2
      ...
      Wend

Příklad switch bloku

Select Case pixelColor
  Case Color_Red   statement1
                   statement2
                   ...

  Case Color_Green statement1
                   statement2
                   ...

  Case Else        statement1
                   statement2
                   ...
End Select

Příklad if bloku

If pixelColor = Color_Red Then
                            statement1
                            statement2
                            ...
                          End If

Elementy tohoto způsobu formátování jsou používány spíše výjimečně, a přestože v některých případech mohou vypadat hezky, je lepší se jim velkým obloukem vyhnout. Mezi hlavní důvody patří velká náročnost na údržbu, ale především fakt, že při vnořování buď dochází k neúměrnému zanoření a nebo se celá struktura rozbije – např. při vnoření více if bloků.

Výsledkem tedy je, že tento formátovací styl je nepřesný, těžko aplikovatelný v konzistentní formě a velmi náročný na údržbu. Steve McConnell ve své knize tvrdí, že viděl tento styl doporučovat jěště v knize z roku 2003.

V podstatě se ukazuje, že jakákoliv forma zarovnávání (mimo prostého odsazení) trpí podobnými problémy a je lepší se jí vyhnout.

Formátování elementů kódu

Délka příkazů

Omezte délku řádku na 80 znaků

Používejte mezery pro větší srozumitelnost

Logické výrazy

while ((pathName[startPath+position]!=';')&&
  ((startPath+position)<pathName.length))
while (( pathName[ startPath + position ] != ';' ) &&
  (( startPath + position ) < pathName.length ))

Indexace polí

grossRate[census[groupId].gender,census[groupId].ageGroup]
grossRate[census[groupId].gender, census[groupId].ageGroup]
grossRate [census [groupId].gender, census [groupId].ageGroup]
grossRate[ census[ groupId ].gender, census[ groupId ].ageGroup ]

Parametry metod

GetCensus(inputFile,employeeCount,employeeData,maxEmployees,inputError)
GetCensus(inputFile, employeeCount, employeeData, maxEmployees, inputError)
GetCensus (inputFile, employeeCount, employeeData, maxEmployees, inputError)
GetCensus( inputFile, employeeCount, employeeData, maxEmployees, inputError )

Rozdělte dlouhé příkazy do více řádků

Složité výrazy

if ((('0' <= inChar) && (inChar <= '9')) || (('a' <= inChar) &&
  (inChar <= 'z')) || (('A' <= inChar) && (inChar <= 'Z')))
if ((( '0' <= inChar ) && ( inChar <= '9' )) ||
  (( 'a' <= inChar ) && ( inChar <= 'z' )) ||
  (( 'A' <= inChar ) && ( inChar <= 'Z' ))
)
if (
  (( '0' <= inChar ) && ( inChar <= '9' )) ||
  (( 'a' <= inChar ) && ( inChar <= 'z' )) ||
  (( 'A' <= inChar ) && ( inChar <= 'Z' ))
)

Udržujte související věci pohromadě

customerBill = PreviousBalance( paymentHistory[
  customerId ]) + LateCharge(  paymentHistory[ customerId ]);
customerBill = PreviousBalance( paymentHistory[ customerId ]) +
  LateCharge( paymentHistory[ customerId ]);

Odsazujte vždy standardním způsobem

Vyhněte se zarovnávání příkazů

customerPurchases = customerPurchases + CustomerSales( customerId );
customerBill      = customerBill + customerPurchases;
totalCustomerBill = customerBill + PreviousBalance( custsomerId ) +
                    LateCharge( customerId );
customerPurchases = customerPurchases + CustomerSales( customerId );
customerBill = customerBill + customerPurchases;
totalCustomerBill = customerBill + PreviousBalance( custsomerId ) +
  LateCharge( customerId );

Odsazujte vždy standardním způsobem

Volání funkcí

DrawLine( window.north, window.south, window.east, window.west,
          currentWidth, currentAttribute );
DrawLine( window.north, window.south, window.east, window.west,
  currentWidth, currentAttribute );
DrawLine(
  window.north, window.south, window.east, window.west,
  currentWidth, currentAttribute
);

Řídící struktury

for ( int employeeNum = employee.first + employee.offset;
      employeeNum < employee.first + employee.offset + employee.total;
      employeeNum++ ) {
for (
  int employeeNum = employee.first + employee.offset;
  employeeNum < employee.first + employee.offset + employee.total;
  employeeNum++
) {     

Odsazujte vždy standardním způsobem

Hlavičky funkcí

bool ReadEmployees( int maxEmployees, EmployeeList * employees,
  EmployeeFile * inputFile, int * employeeCount, bool * isInputError )
bool ReadEmployees( int maxEmployees,
                    EmployeeList * employees,
                    EmployeeFile * inputFile,
                    int * employeeCount,
                    bool * isInputError )
bool ReadEmployees (
  int maxEmployees,
  EmployeeList * employees,
  EmployeeFile * inputFile,
  int * employeeCount,
  bool * isInputError
)

Používejte pouze jednu deklaraci na řádek

Normální datové typy

int rowIndex, columnIndex;
Color previousColor, currentColor, nextColor;
Point previousTop, previousBottom, currentTop, currentBottom,
  nextTop, nextBottom;
Font previousTypeface, currentTypeface, nextTypeface;
Color choices[ NUM_COLORS ];
int rowIndex;
int columnIndex;
Color previousColor;
Color currentColor;
...
Color choices[ NUM_COLORS ];

Ukazatelové datové typy

File * inputFile, outputFile;
File * inputFile;
File * outputFile;