Cod împachetat

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită la 17 februarie 2021; verificările necesită 7 modificări .

Codul mirosului (codul mirosului , codul urat mirositor cod englezesc  miros ) este un termen pentru codul cu semne (mirosuri) de probleme în sistem. A fost introdus de Kent Beck [1] și folosit de Martin Fowler în cartea sa Refactoring. Îmbunătățirea codului existent [1] .

Mirosurile de cod  sunt semne cheie ale necesității de refactorizare [2] . Există mirosuri specifice atât paradigmelor de programare, cât și limbajelor specifice . Principala problemă cu care se confruntă dezvoltatorii atunci când se confruntă cu mirosurile de cod este că criteriile pentru oportunitatea refactorizării nu pot fi formalizate clar fără a face apel la estetică și la un simț convențional al frumosului. Mirosurile de cod nu sunt un set de reguli clare, ci o descriere a locurilor cărora trebuie să le acordați atenție la refactorizare [3] . Sunt ușor de detectat, dar nu indică în toate cazurile probleme [1] .

Codul de miros duce la ruperea codului, dezvoltatorii ar trebui să se străduiască să elimine mirosurile prin aplicarea de refactorizări simple sau multiple [4] . Procesul de refactorizare elimină mirosurile de cod, ceea ce permite aplicației să continue să evolueze la aceeași viteză sau mai mare. Lipsa refactorizării regulate poate paraliza complet un proiect în timp, așa că mirosurile de cod trebuie eliminate din timp [2] . Există instrumente pentru a găsi și a remedia mirosurile de cod [5] , dar experiența arată că niciun tablou de bord nu poate concura cu intuiția umană bazată pe informații [6] .

Cod mirosuri

Miros general de cod orientat pe obiecte

Dublarea codului

Dublarea codului este utilizarea acelorași structuri de cod în mai multe locuri. Combinarea acestor structuri va îmbunătăți codul programului [6] .

Exemple de duplicare și metode de eliminare a acestora:

  • Aceeași expresie este prezentă în două metode din aceeași clasă: trebuie să aplicați „Extract Method” și să apelați codul metodei create din ambele puncte;
  • Aceeași expresie există în două subclase la același nivel: trebuie să aplicați Extract Method pe ambele clase, urmată de Pull Up Field sau Form Template Method , dacă codul este similar, dar nu este exact același. Dacă ambele metode fac același lucru folosind algoritmi diferiți, puteți alege cel mai clar dintre acești algoritmi și puteți aplica „Algoritmul de substituție” (Algoritmul de înlocuire);
  • Codul duplicat este în două clase diferite: trebuie să aplicați Extract Class într-o clasă și apoi să utilizați noua componentă într-o altă [6] .
Metoda lungă

Dintre programele obiect, programele cu metode scurte trăiesc cel mai mult . Cu cât procedura este mai lungă, cu atât este mai dificil de înțeles. Dacă metoda are un nume bun, atunci nu trebuie să vă uitați la corpul ei [3] .

Trebuie urmată o euristică: dacă simți nevoia să comentezi ceva, trebuie să scrii o metodă. Are sens să separă chiar și o linie într-o metodă dacă are nevoie de clarificare [7] .

  • Pentru a reduce metoda, este suficient să aplicați Metoda Extract;
  • Dacă variabilele și parametrii locali împiedică extragerea metodei, puteți utiliza Replace Temp with Query, Introduce Parameter Object și Preserve Whole Object [3] ;
  • Declarațiile și buclele condiționate indică posibilitatea de separare într-o metodă separată. Decompose Conditional este potrivit pentru lucrul cu expresii condiționale. Pentru a lucra cu ciclul - „Extract Method” (Extract Method) [7] .
Clasa mare

Când o clasă implementează prea multă funcționalitate, luați în considerare subclasarea unei părți din cod. Acest lucru va salva dezvoltatorii de un număr excesiv de atribute pe care le are o clasă și de duplicarea codului [7] .

  • Pentru a reduce o clasă, utilizați Extract Class sau Extract Subclass. În același timp, ar trebui să se acorde atenție comunității în numele atributelor și dacă clasa le folosește pe toate în același timp [3] ;
  • Dacă clasa mare este o clasă GUI , este posibil să doriți să mutați datele și comportamentul acesteia într-un obiect de domeniu separat. Cu toate acestea, poate fi necesar să stocați copii ale unor date în două locuri și să le asigurați consistența. Datele observate duplicate sugerează un mod în care acest lucru poate fi realizat [8] .
Lista lungă de opțiuni

Listele lungi de parametri sunt greu de înțeles, inconsecvente și dificil de utilizat. Utilizarea obiectelor permite, în cazul unor modificări ale datelor transmise, modificarea doar a obiectului în sine. Când lucrați cu obiecte, ar trebui să treceți suficient pentru ca metoda să poată obține datele de care are nevoie [8] .

  • „Înlocuiește parametrul cu metoda” este folosit când puteți obține date apelând o metodă pe un obiect. Acest obiect poate fi un câmp sau un alt parametru.
  • Păstrarea întregului obiect vă permite să luați un grup de date primite de la un obiect și să îl înlocuiți cu obiectul în sine.
  • „Introduce Parameter Object” este folosit dacă există mai multe elemente de date fără un obiect logic [8] .
Modificări divergente

Problema apare atunci când, la modificarea sistemului, este imposibil să se aloce un anumit loc care trebuie schimbat. Aceasta este o consecință a structurii software proaste [8] sau a programării copy-paste .

  • Dacă setul de metode trebuie schimbat de fiecare dată când anumite modificări sunt aduse codului, atunci se aplică Extract Class (De exemplu, trei metode se schimbă de fiecare dată când se conectează o nouă bază de date și patru când se adaugă un instrument financiar) [3] ] .
Tragere cu pușca

Orice modificare implică multe mici modificări la un număr mare de clase. Shotgun este similar cu modificarea divergentă, dar este opusul ei. O modificare divergentă apare atunci când există o clasă care face multe modificări diferite, în timp ce Shotgun este o modificare care afectează multe clase [9] .

  • Mutați toate modificările într-o singură clasă va permite „Metoda de mutare” (Metoda de mutare) și „Mutați câmpul” (Mutați câmpul);
  • Dacă nu există o clasă adecvată, atunci ar trebui creată o nouă clasă;
  • Dacă este necesar, utilizați Inline Class [3] .
Funcții Envy

Metoda accesează datele altui obiect mai des decât datele proprii [3] .

  • „Metoda de mutare” este utilizată dacă metoda trebuie mutată în mod explicit într-o altă locație;
  • Metoda de extragere se aplică unei părți de metodă numai dacă acea parte accesează datele altui obiect;
  • Metoda folosește funcțiile mai multor clase: se determină care clasă conține cele mai multe date, iar metoda este plasată în clasă împreună cu aceste date, sau folosind metoda Extract, metoda este împărțită în mai multe părți și acestea sunt plasate în locuri diferite [10 ] .

O regulă fundamentală este că lucrurile care se schimbă în același timp trebuie păstrate într-un singur loc. Datele și funcțiile care folosesc acele date se schimbă de obicei împreună, dar există și excepții [10] .

Grupuri de date

Grupurile de date care apar împreună ar trebui transformate într-o clasă separată [10] .

  • „Metoda de extragere” este folosită pentru câmpuri;
  • „Introduce Parameter Object” sau „Preserve Whole Object” pentru parametrii metodei [11] .

Un test bun este să eliminați una dintre valorile datelor și să vedeți dacă celelalte mai au sens. Dacă nu, este un semn sigur că datele cer să fie îmbinate într-un obiect [10] .

Obsesia pentru tipurile elementare

Problema este legată de utilizarea unor tipuri elementare în locul obiectelor mici pentru sarcini mici, precum valută, intervale, șiruri speciale pentru numere de telefon etc.

  • „Înlocuiește valoarea datelor cu obiect”;
  • „Înlocuirea unei matrice cu un obiect” (Înlocuire matrice cu obiect);
  • Dacă este un cod de tip, atunci utilizați Înlocuire cod de tip cu clasă, Înlocuire cod de tip cu subclase sau Înlocuire cod de tip cu stat/strategie) [3] .
declarații switch

Un semn distinctiv evident al codului orientat pe obiecte este utilizarea relativ rară a declarațiilor switch (sau case) . Adesea, același bloc de comutare ajunge împrăștiat în locuri diferite din program. Când adăugați o nouă opțiune, trebuie să căutați toate aceste blocuri de comutare și să le modificați. De regulă, când observați un bloc comutator, ar trebui să vă gândiți la polimorfism [12] .

  • Dacă comutatorul comută după codul de tip, atunci ar trebui să utilizați „Înlocuire cod de tip cu subclase” sau „Înlocuire cod de tip cu stat/strategie”;
  • Poate fi necesar să „Extract Method” și „Move Method” pentru a izola comutatorul și a-l pune în clasa potrivită;
  • După configurarea structurii de moștenire, ar trebui să utilizați Înlocuire condițional cu polimorfism [3] .
Ierarhii de moștenire paralele

În codul cu acest miros, de fiecare dată când subclasați una dintre clase, trebuie să creați o subclasă a altei clase [12] .

  • O strategie comună de deduplicare este de a face ca instanțe ale unei ierarhii să se refere la instanțe ale altei ierarhii și apoi să eliminați ierarhia din clasa de referință utilizând Metoda de mutare și Câmpul de mutare [12] .
Clasa leneșă

O clasă ale cărei costuri de existență nu sunt acoperite de funcțiile pe care le îndeplinește ar trebui eliminată [12] .

  • Dacă există subclase cu funcționalitate insuficientă, încercați Collapse Hierarchy;
  • Componentele aproape inutile ar trebui supuse clasei Inline [12] .
Generalitate teoretică

Acest caz apare atunci când, la un moment dat în viața unui program, este prevăzut un set de mecanisme de care ar putea avea nevoie de anumite funcționalități viitoare. Ca urmare, programul devine mai dificil de înțeles și de întreținut [13] .

  • Pentru clasele abstracte neutilizate, utilizați Collapse Hierarchy;
  • Delegarea inutilă poate fi eliminată folosind Inline Class;
  • Metodele cu parametri neutilizați trebuie supuse unui „Remove Parameter” [3] .
Câmp de timp

Câmpurile temporare sunt câmpuri de care un obiect are nevoie doar în anumite circumstanțe. Această stare de lucruri este greu de înțeles, deoarece un obiect este de așteptat să aibă nevoie de toate câmpurile sale [14] .

  • Câmpurile temporare și tot codul care lucrează cu acestea ar trebui plasate într-o clasă separată folosind Extract Class;
  • Puteți elimina codul executabil condiționat folosind Introduce Null Object pentru a crea o componentă alternativă [13] .
Lanț de apeluri

Un lanț de apeluri apare atunci când un client solicită un alt obiect de la un obiect, un alt obiect solicită un alt obiect și așa mai departe. Astfel de secvențe de apeluri înseamnă că clientul este asociat cu navigarea în structura clasei. Orice modificare a legăturilor intermediare înseamnă necesitatea modificării clientului [13] .

  • Pentru a elimina lanțul de apeluri, se folosește tehnica Ascundere Delegat [13] .
Intermediar

Utilizarea excesivă a delegării poate duce la clase în care majoritatea metodelor constau doar în apelarea unei metode din altă clasă [13] .

  • Dacă o clasă delegă majoritatea metodelor unei alte clase, trebuie să utilizați „Eliminare Middle Man” [15] .
Apropiere deplasată

„Proximitatea deplasată” apare atunci când clasele sunt mai des decât ar trebui să fie scufundate în părți închise unele ale altora [15] .

  • Puteți scăpa de „Proximitatea inadecvată” folosind „Metoda de mutare” (Metoda de mutare) și „Câmpul de mutare” (Câmpul de mutare);
  • Dacă este posibil, ar trebui să apelați la „Change Bidirectional Association to Unidirectional”, „Extract Class” sau folosiți „Hide Delegate” [15] .
Clase alternative cu interfețe diferite

Două clase în care o parte a funcționalității este comună, dar metodele care o implementează au parametri diferiți [16] .

  • Aplicați „Metoda Redenumire” tuturor metodelor care efectuează aceleași acțiuni, dar diferă în semnături [15] .
Incompletitudinea clasei bibliotecii

Bibliotecile încetează să îndeplinească cerințele utilizatorilor după un timp. Soluția firească este schimbarea unor lucruri din biblioteci, dar nu schimbarea claselor din bibliotecă. Ar trebui să utilizați metode de refactorizare special concepute pentru acest scop [16] .

  • Dacă trebuie să adăugați câteva metode, utilizați „Introduceți metoda străină”;
  • Dacă trebuie să schimbați serios comportamentul clasei, utilizați „Introduce Local Extension” (Introduce Local Extension) [16] .
Clase de date

Clasele de date sunt clase care conțin doar câmpuri și metode pentru a le accesa, sunt pur și simplu containere pentru date folosite de alte clase [16] .

  • Aplicați câmpul încapsulat și colecția încapsulată [3] .
Renuntarea la mostenire

Dacă copilul folosește doar o mică parte din metodele și proprietățile moștenite ale părintelui, acesta este un semn al unei ierarhii proaste.

  • Trebuie să creați o clasă nouă la același nivel cu copilul și să utilizați Push Down Method și Push Down Field pentru a împinge toate metodele inactive în ea. Acest lucru asigură că clasa părinte conține doar ceea ce este partajat [17] .
Comentarii

Adesea, comentariile joacă rolul unui „deodorant” al codului, care apare în el doar pentru că codul este rău. Când simțiți nevoia să scrieți un comentariu, încercați să vă restructurați codul, astfel încât orice comentarii să devină redundante [17] .

  • Dacă mai aveți nevoie de un comentariu pentru a explica acțiunile blocului, încercați să utilizați metoda de extragere;
  • Dacă o metodă este deja evidențiată, dar mai aveți nevoie de un comentariu pentru a explica funcționarea acesteia, utilizați Metoda Redenumire;
  • Dacă doriți să precizați câteva reguli despre starea necesară a sistemului, utilizați Introduce Assertion [17] .

Vezi și

  • Antimodel
  • Categorie:Principii de programare
  • Instrumente de analiză a codului static

Note

  1. 1 2 3 Martin, 1999 .
  2. 1 2 Vigorous Hive_CodeSmell .
  3. 1 2 3 4 5 6 7 8 9 10 11 Cod urat mirositor .
  4. Counsell_Code Smells, 2010 .
  5. devconf .
  6. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 54.
  7. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 55.
  8. 1 2 3 4 Martin Fowler_Refactoring, 2003 , p. 56.
  9. Martin Fowler_Refactoring, 2003 , p. 56-57.
  10. 1 2 3 4 Martin Fowler_Refactoring, 2003 , p. 57.
  11. Smelly Code , p. 57.
  12. 1 2 3 4 5 Martin Fowler_Refactoring, 2003 , p. 58.
  13. 1 2 3 4 5 Martin Fowler_Refactoring, 2003 , p. 59.
  14. Câmp temporar .
  15. 1 2 3 4 Martin Fowler_Refactoring, 2003 , p. 60.
  16. 1 2 3 4 Refactorizarea codului .
  17. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 61.

Literatură

  • Fowler, M. Capitolul 3. Smelly Code // Refactorizare. Îmbunătățirea codului existent = Refactoring: Improving Design of Existing Code / Per. din engleza. S. Makkaveeva. - Ed. I. - Sankt Petersburg. : Symbol-Plus, 2003. - S. 54-62. - 432 p. — ISBN 5-93286-045-6 .
  • Mantyla MV, Vanhanen J., Lassenius C. Bad smells-humans as code critics  (engleză)  // Software Maintenance, 2004. Proceedings. A 20-a Conferință Internațională IEEE pe: jurnal. - 2004. - P. 399-408 . — ISSN 1063-6773 . Arhivat din original la 1 august 2014.

Link -uri

  • Câmp temporar . codingcraft.ru Preluat: 5 noiembrie 2013.
  • CodeSmell  (engleză) . Martinfowler.com. Preluat: 13 octombrie 2013.
  • Miroase a cod rău (downlink) . vihv.org. Consultat la 13 octombrie 2013. Arhivat din original pe 9 decembrie 2013. 
  • Cod Smell  (engleză) . Cunningham & Cunningham Inc. (c2.com). Preluat: 23 noiembrie 2013.