Descompunerea ierarhică
Nu ar trebui să începeți imediat să scrieți cursuri pentru aplicația dvs. Mai întâi trebuie proiectat. Designul ar trebui să se încheie cu o arhitectură atentă. Și pentru a obține această arhitectură, trebuie să descompuneți constant sistemul.
Descompunerea trebuie efectuată ierarhic - în primul rând, sistemul este împărțit în module / subsisteme funcționale mari care descriu funcționarea sa în cea mai generală formă. Apoi modulele rezultate sunt analizate mai detaliat și împărțite în submodule sau obiecte.
Înainte de a selecta obiectele, împărțiți sistemul în blocuri semantice de bază, cel puțin mental. În aplicațiile mici, acest lucru este de obicei foarte ușor de făcut: câteva nivele de ierarhie sunt destul de suficiente, deoarece sistemul este mai întâi împărțit în subsisteme / pachete, iar pachetele sunt împărțite în clase.
![Descompunerea ierarhică](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
Această idee nu este atât de banală pe cât pare. De exemplu, care este esența unui „model arhitectural” atât de comun ca Model-View-Controller (MVC)?
Totul este despre separarea prezentării de logica afacerii . În primul rând, orice aplicație de utilizator este împărțită în două module - unul este responsabil pentru implementarea logicii de afaceri în sine (Model), iar al doilea este responsabil pentru interacțiunea cu utilizatorul (Interfață de utilizator sau Vizualizare).
Apoi se dovedește că modulele trebuie cumva să interacționeze, pentru asta adaugă un Controller, a cărui sarcină este să gestioneze interacțiunea modulelor. Tot în versiunea mobilă (clasică) a MVC, i se adaugă modelul Observer, astfel încât View-ul să poată primi evenimente din model și să modifice datele afișate în timp real.
Modulele tipice de nivel superior, obținute ca urmare a primei diviziuni a sistemului în cele mai mari componente, sunt exact:
- Lociga afacerii;
- Interfața cu utilizatorul;
- Bază de date;
- Sistem de mesagerie;
- Container de obiecte.
Prima împărțire împarte de obicei întreaga aplicație în 2-7 (maximum 10 părți). Dacă îl împărțim în mai multe părți, atunci va exista dorința de a le grupa și vom obține din nou 2-7 module de nivel superior.
Descompunerea functionala
Divizarea în module/subsisteme se face cel mai bine pe baza sarcinilor pe care le rezolvă sistemul . Sarcina principală este împărțită în subsarcinile sale constitutive, care pot fi rezolvate/efectuate în mod autonom, independent unele de altele.
Fiecare modul ar trebui să fie responsabil pentru rezolvarea unor subsarcini și să-și îndeplinească funcția corespunzătoare . Pe lângă scopul funcțional, modulul se caracterizează și printr-un set de date necesare pentru a-și îndeplini funcția, adică:
Modul = Funcție + Date necesare pentru a-l executa.
Dacă descompunerea în module se face corect, atunci interacțiunea cu alte module (responsabile pentru alte funcții) va fi minimă. Poate fi, dar absența sa nu ar trebui să fie critică pentru modulul tău.
Un modul nu este o bucată de cod arbitrară, ci o unitate de program (subprogram) completă și semnificativă din punct de vedere funcțional, care oferă o soluție pentru o anumită sarcină și, în mod ideal, poate funcționa independent sau într-un alt mediu și poate fi reutilizată. Modulul ar trebui să fie un fel de „integritate capabilă de o relativă independență în comportament și dezvoltare”. (Christopher Alexander)
Astfel, descompunerea competentă se bazează, în primul rând, pe analiza funcțiilor sistemului și a datelor necesare îndeplinirii acestor funcții. Funcțiile în acest caz nu sunt funcții de clasă și module, deoarece nu sunt obiecte. Dacă aveți doar câteva clase într-un modul, atunci ați exagerat.
Conectivitate puternică și slabă
Este foarte important să nu exagerați cu modularizarea. Dacă îi oferi unui începător o aplicație Spring monolitică și îi cereți să o despartă în module, atunci el va scoate fiecare Spring Bean într-un modul separat și va considera că munca lui este terminată. Dar nu este.
Principalul criteriu pentru calitatea descompunerii este modul în care modulele sunt concentrate pe rezolvarea sarcinilor lor și sunt independente.
Aceasta este de obicei formulată după cum urmează: „Modulele obținute ca rezultat al descompunerii ar trebui să fie maxim conjugate intern (coeziune internă ridicată) și minim interconectate între ele (cuplaj extern scăzut).”
Coeziune ridicată, coeziune ridicată sau „coeziune” în cadrul modulului, indică faptul că modulul este concentrat pe rezolvarea unei probleme restrânse și nu este angajat în îndeplinirea unor funcții eterogene sau responsabilități necorelate.
Coeziunea caracterizează gradul în care sarcinile îndeplinite de modul sunt legate între ele.
O consecință a Înaltei Coeziuni este Principiul Responsabilității Unice - primul dintre cele cinci principii SOLIDE , conform căruia orice obiect/modul ar trebui să aibă o singură responsabilitate și nu ar trebui să existe mai mult de un motiv pentru modificarea acesteia.
Low Coupling , cuplare liberă, înseamnă că modulele în care este împărțit sistemul ar trebui să fie, dacă este posibil, independente sau slab cuplate între ele. Ar trebui să poată interacționa, dar în același timp să se cunoască cât mai puțin unul despre celălalt.
Fiecare modul nu trebuie să știe cum funcționează celălalt modul, în ce limbă este scris și cum funcționează. Adesea, pentru a organiza interacțiunea unor astfel de module, se folosește un anumit container, în care sunt încărcate aceste module.
Cu un design adecvat, dacă schimbați un modul, nu va trebui să editați altele, sau aceste modificări vor fi minime. Cu cât cuplajul este mai slab, cu atât este mai ușor să scrieți/înțelegeți/extindeți/reparați programul.
Se crede că modulele bine proiectate ar trebui să aibă următoarele proprietăți:
- Integritate funcțională și completitudine - fiecare modul implementează o funcție, dar o implementează bine și complet, modulul realizează în mod independent un set complet de operațiuni pentru a-și implementa funcția.
- O intrare și o ieșire - la intrare, modulul de program primește un anumit set de date inițiale, efectuează o procesare semnificativă și returnează un set de date rezultate, adică este implementat principiul standard IPO - intrare -\u003e proces -\u003e ieșire.
- Independență logică - rezultatul muncii modulului de program depinde numai de datele inițiale, dar nu depinde de munca altor module.
- Legături slabe de informații cu alte module - schimbul de informații între module ar trebui redus la minimum dacă este posibil.
Este foarte dificil pentru un începător să înțeleagă cum să reducă și mai mult conectivitatea modulelor. Parțial, aceste cunoștințe vin odată cu experiența, parțial - după citirea cărților inteligente. Dar cel mai bine este să analizați arhitecturile aplicațiilor existente.
Compoziția în loc de moștenire
Descompunerea competentă este un fel de artă și o sarcină dificilă pentru majoritatea programatorilor. Simplitatea este înșelătoare aici, iar greșelile sunt costisitoare.
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
Se întâmplă ca modulele dedicate să fie puternic cuplate între ele și să nu poată fi dezvoltate independent. Sau nu este clar de ce funcție este responsabil fiecare dintre ei. Dacă întâmpinați o problemă similară, atunci cel mai probabil partiționarea în module a fost făcută incorect.
Ar trebui să fie întotdeauna clar ce rol joacă fiecare modul . Cel mai de încredere criteriu că descompunerea se face corect este dacă modulele sunt subrutine independente și valoroase care pot fi utilizate izolat de restul aplicației (și prin urmare pot fi reutilizate).
Când descompuneți un sistem, este de dorit să verificați calitatea acestuia punându-vă întrebările: „Ce sarcină îndeplinește fiecare modul?”, „Cât de ușor sunt modulele de testat?”, „Este posibil să utilizați modulele singure?” sau într-un alt mediu?" îi afectează pe alții?"
Trebuie să încercați să păstrați modulele cât mai autonome posibil . După cum am menționat anterior, acesta este un parametru cheie pentru descompunerea corectă . Prin urmare, trebuie efectuată astfel încât modulele să fie inițial slab dependente unele de altele. Dacă ai reușit, atunci ești grozav.
Dacă nu, atunci nici aici nu este totul pierdut. Există o serie de tehnici și modele speciale care vă permit să minimizați și să slăbiți în continuare legăturile dintre subsisteme. De exemplu, în cazul MVC, modelul Observer a fost folosit în acest scop, dar sunt posibile alte soluții.
Se poate spune că tehnicile de decuplare constituie principalul „kit de instrumente al arhitectului”. Este necesar doar să înțelegem că vorbim despre toate subsistemele și este necesară slăbirea conexiunii la toate nivelurile ierarhiei , adică nu numai între clase, ci și între module de la fiecare nivel ierarhic.