Using object indexes vs more readable alternatives

De ceva vreme am dificultati in a vedea cod care contine indecsi fara niciun fel de context. In anumite situatii nu pot fi evitati, dar in cele mai multe exista variante mai readable.

In procesarea unul string, spre exemplu, cum preferati:
string.split(substring)[index]
sau alternativa
string.replace(substring)
plus o eventuala extra procesare daca este cazul.

La liste, array, tuple etc:
name = contact_details[0]
sau alternativa
name, address = contact_details (pe frontend se mai numeste si dispatching)
Lista de exemple poate continua, insa cred ca m-am facut inteles cu astea 2.

Eu prefer si folosesc varinata fara index. Mi se pare ca este mai usor de inteles. Uneori mai folosesc si indexul, dar scriu un comentariu cu ce reprezinta acea pozitie.

Cred ca destructure se cheama treaba aia. Dar nu toate limbajele o suporta (Java :smiley: ). Dar parca in Python, am vazut-o prin niste scripturi.

1 Like

Nu cred că sunt neapărat interschimbabile. Nu tot timpul, cel puțin.

De exemplu, nu poți înlocui split cu destructurare, pentru că nu ai… unde.

Dar totul depinde de context. De exemplu are mai mult sens să faci ceva de genul:

.map(string => string.split(substring)[index])

Decât să atribui split-ul unei variabile, apoi să întorci index-ul din variabilă. „Economisești” vreo 4 linii. :slight_smile:

Aș zice că problemele le ai cel mai des în blocurile de cod mari, cu zeci de linii de cod. Greșesc?

Da, destructuring.

python si javascript o suporta.

Split este de aceeasi parte “readable” a baricadei cu destructuring si servesc unor scopuri diferite. Eu ma refeream de a folosi una din aceste variante in locul notatiilor cu indecsi.

Ai intuit bine.

Eh, atunci este evident unde e problema :slight_smile:

Uncle Bob zice despre funcții:

The first rule of functions is that they should be small.

The second rule of functions is that they should be smaller than that.

Dar se aplică la orice bloc de cod: loop-uri, condiții etc.

Eu personal încerc să limitez blocurile la 20-30 linii. În cazuri mai deosebite, pot ajunge la ~50 linii (atâtea îmi intră pe ecran). În cazuri excepționale depășesc numărul ăsta de linii, cu condiția să am zero logică (e.g. când compun/returnez un array).

Un alt lucru pe care îl poți face pentru a simplifica blocuri de cod este să inversezi condițiile. Adică în loc de:

if( foo ) {
  // 30 linii de cod
} else {
  return
}

Poți face:

if( !foo ) return
// restul codului

Nu-ți scurtează codul, dar scapi de un nivel de indent, putând astfel să parsezi mental toată povestea.

Valabil pentru toate structurile din care poți „ieși”: funcții, loop-uri (cu continue), switch (cu break) etc.

3 Likes

You have a point here. Atat ca, desi ii spunem OOP, este de fapt de multe ori combinatie intre OOP si procedural, si uneori esti obligat sa rezolvi conditia pozitiva pentru outcomeul dorit.
Altfel da, este o modalitate de a face codul more readable.

1 Like

Si mie mi se pune comment la review daca nu am un singur return per function.

Păi și? Treaba asta e (sau poate fi) de bine, te încurajează să spargi codul în mai multe părți :slight_smile:

Muți toată logica din blocul ăla într-o altă funcție după care faci:

return !foo ? false : thatLogic()

Ești încurajat să reduci mărimea funcțiilor :slight_smile:

Dacă folosești OOP, poți recurge la polimorfism și elimini condițiile de tot.

E exemplul meu favorit din defensive programming. In principal pt ca iti permite sa vezi care e de fapt “happy flow”-ul la un nivel de indentare corect. Restul, partile anterioare de unde “iesi” inainte sa atingi happy flow, pot fi apoi mult mai usor refactorizate intr-o metoda separata de ex denumita “validateCeva” si iti scurtezi metoda ta punand garda in felul asta.

3 Likes

Exceptie functiile recursive

La munca mai folosesc treaba asta

Cel mai des folosesc checkNotNull(value) si checkArgument()

public vod foo(Foo fm int bar) {
 checkNotNull(f, "f must be not null");
checkArgument(bar >= 0, "Argument was %s but expected nonnegative", bar);
...
}