Criterii pentru design prost

Viața funcționează destul de simplu: de multe ori, pentru a fi inteligent, trebuie doar să nu faci prostii. Acest lucru este valabil și pentru dezvoltarea de software: în cele mai multe cazuri, pentru a face ceva bine, trebuie doar să nu o faci rău.

Majoritatea programatorilor au avut experiență cu părți ale sistemului care au fost proiectate prost. Dar și mai trist, majoritatea dintre voi veți avea experiența tristă de a realiza că voi ați fost autorii unui astfel de sistem. Ne-am dorit ce e mai bun, dar a ieșit ca întotdeauna.

Majoritatea dezvoltatorilor nu aspiră la o arhitectură proastă, iar pentru multe sisteme ajunge un punct în care încep să spună că arhitectura sa este teribilă. De ce se întâmplă asta? Designul arhitecturii a fost rău de la început sau a devenit rău în timp?

Rădăcina acestei probleme este lipsa unei definiții a designului „rău”.

Mi se pare că înțelegerea calității designului și a motivelor „decăderii” acestuia sunt cele mai importante calități pentru orice programator. Ca în majoritatea celorlalte cazuri, principalul lucru este identificarea problemei și va fi o problemă de tehnologie pentru a o rezolva.

Definiția „proiectului prost”

Dacă decideți să vă lăudați cu codul în fața unui coleg programator, cel mai probabil veți primi ridicol ca răspuns: „Cine face asta?”, „De ce este așa? și „Aș face lucrurile altfel”. Acest lucru se întâmplă foarte des.

Toți oamenii sunt diferiți, dar încă scrii codul pentru colegii tăi programatori, așa că în procesul de dezvoltare a fiecărei caracteristici, ai întotdeauna nevoie de o fază de revizuire când alți oameni se uită la codul tău.

Dar chiar dacă multe lucruri pot fi făcute în moduri diferite, există un set de criterii asupra cărora toți dezvoltatorii ar fi de acord. Orice bucată de cod care își satisface cerințele, dar încă prezintă una (sau mai multe) caracteristici este un design prost.

Design prost:

  • Este dificil de schimbat deoarece orice modificare afectează prea multe alte părți ale sistemului. ( Rigiditate , Rigiditate).
  • Când se fac modificări, alte părți ale sistemului se întrerup în mod neașteptat. ( Fragilitate , Fragilitate).
  • Codul este greu de reutilizat într-o altă aplicație, deoarece este prea greu să-l scoți din aplicația curentă. ( Imobilitatea , Imobilitatea).

Și lucrul amuzant este că este aproape imposibil să găsești o piesă a sistemului care să nu conțină niciuna dintre aceste caracteristici (adică să fie flexibilă, fiabilă și reutilizabilă), să îndeplinească cerințele și, în același timp, designul său este prost. .

Astfel, putem folosi aceste trei caracteristici pentru a determina fără ambiguitate dacă un design este „rău” sau „bun”.

Cauzele „proiectării proaste”

Ce face un design rigid, fragil și imobil? Interdependență rigidă a modulelor.

Un design este rigid dacă nu poate fi schimbat cu ușurință. Această rigiditate se datorează faptului că o singură modificare a unei bucăți de cod într-un sistem țesut are ca rezultat modificări în cascadă în modulele dependente. Acest lucru se întâmplă întotdeauna când o persoană lucrează la cod.

Acest lucru complică imediat întregul proces de dezvoltare comercială: atunci când numărul de modificări în cascadă nu poate fi prezis de către proiectant sau dezvoltator, este imposibil să se estimeze impactul unei astfel de modificări. Prin urmare, ei încearcă să amâne astfel de schimbări pe termen nelimitat.

Și acest lucru, la rândul său, face ca costul schimbării să fie imprevizibil. Confruntați cu o astfel de incertitudine, managerii sunt reticenți în a face modificări, astfel încât designul devine oficial rigid.

La un moment dat, proiectul tău trece de „orizontul evenimentelor” și este sortit să cadă în „gaura neagră” a arhitecturii rigide.

Fragilitatea este tendința unui sistem de a se defecta în mai multe locuri după o singură schimbare. De obicei, probleme noi apar în locuri care nu au legătură conceptual cu locul schimbării. O astfel de fragilitate subminează serios încrederea în proiectarea și întreținerea sistemului.

Acesta a fost de obicei cazul când nu existau metode private. Este suficient să faci publice toate metodele și vei fi condamnat la apariția unei arhitecturi fragile. Încapsularea ajută la rezolvarea acestui lucru la nivel micro. Dar la nivel macro, ai nevoie de o arhitectură modulară.

Atunci când un proiect are o arhitectură fragilă, dezvoltatorii nu pot garanta calitatea produsului.

Schimbările simple într-o parte a aplicației duc la erori în alte părți care nu au legătură. Corectarea acestor erori duce la și mai multe probleme, iar procesul de escortă se transformă într-un câine celebru care își urmărește propria coadă.

Designul este imobil atunci când părțile necesare ale sistemului sunt puternic legate de alte detalii nedorite. Prea mult din propriul lor cod, propriile lor abordări și soluții unice.

Îți amintești de jurnalul JUL, ai cărui dezvoltatori au venit cu propriile lor niveluri de înregistrare fără un motiv întemeiat? Acesta este tocmai cazul.

Pentru a-i oferi unui designer o idee despre cât de ușor este să reutilizați un design existent, este suficient să vă gândiți cât de ușor va fi să îl utilizați într-o nouă aplicație.

Dacă designul este strâns cuplat, atunci acest designer va fi îngrozit de cantitatea de muncă necesară pentru a separa părțile necesare ale sistemului de detaliile inutile. În cele mai multe cazuri, un astfel de design nu este reutilizabil, deoarece costul separării lui depășește dezvoltarea lui de la zero.

Relevanţă

Totul se schimbă, dar totul rămâne la fel. (proverb chinezesc)

Au fost ridicate întrebări foarte bune mai sus. Care sunt pericolele sistemelor fragile și rigide? Da, pentru că procesul de gestionare a unui astfel de proiect devine imprevizibil și incontrolabil. Și prețul este exorbitant.

Cum poate un manager să dea sau să nu dea voie pentru a adăuga o funcție dacă nu știe cât timp va dura de fapt? Cum să prioritizați sarcinile dacă nu puteți estima în mod adecvat timpul și complexitatea implementării lor?

Și cum pot dezvoltatorii să plătească aceeași datorie tehnică atunci când o vom achita și nu putem înțelege cât de mult vom rake până când vom rake?

Problemele legate de reutilizarea sau testarea codului sunt, de asemenea, foarte relevante. Testele unitare servesc nu numai la testarea unor ipoteze despre unitatea testată, ci și la determinarea gradului de coeziune a acesteia și pot servi ca indicator de reutilizare.

Iată un citat de la Bob Martin pentru acest caz: „Pentru a vă reutiliza codul, trebuie să faceți efortul de a-l reutiliza mai puțin decât costul dezvoltării de la zero . ” Altfel, nimeni nu se va deranja nici măcar cu această chestiune.

Utilizarea principiilor și modelelor de design servește unui singur scop - de a face designul bun. Dacă utilizarea lor nu vă oferă niciun beneficiu (sau invers, încalcă principiile „proiectării bune”), atunci ceva în conservatorul dvs. nu este corect și, poate, instrumentul a început să fie folosit în alte scopuri.