Dragă programatorule, dacă îți vine a copy-paste, fă refactor!

M-am săturat de cod prost scris ca de mere pădurețe. Nu mă refer la cod nefuncțional sau cu bug-uri. Mă refer la metodologia copy-paste la care se apelează intensiv din când în când. După care apucă-te și modifică ceva pentru a adăuga chestii noi. M-am săturat de gândire non-DRY datorită căreia apuc să modific în 5 locuri și 3 fișiere pentru a pune o chestie amărâtă care să arate la fel peste tot. În concluzie, pe lângă defularea de mai sus, m-am hotărât să mai dau niște idei.

Pe alocuri plângerile mele au avut succes. Acum două zile colegii de echipă mă ascultau în timp ce modificam niște chestii, iar involuntar am zis: iar de aici copiez dincolo … touche: “Ce-ai zis mă? Să copiezi?”. Exact ce ziceam mai sus … câteodată și mie îmi vine greu să nu scriu cod prost. Dar eforturile susținute = evoluție. În concluzie am luat linia aceea lungă (un apel înlănțuit de proceduri) și am pus-o într-o nouă metodă.

În concluzie vreo câteva idei, departe de a oferi o imagine completă:

  • dacă îți vine să faci copy-paste, fie ele și 3 linii de cod sau una lungă, înseamnă ca ai nevoie de un mic refactor.
  • o arhitectură bună, modulară, a aplicației, DRY (și preferabil KISS) compliant, duce la o mentenanță mai ușoară. Pentru a modifica ceva nu este nevoie să cauți toate instanțele aceleiași bucăți de cod.
  • dacă acea parte de ‘unknown’ umbrește puterea de a-ți crea arhitectura înainte de a o implementa, atunci orice model repetitiv din cod stă bine într-o metodă separată.
  • caută să înțelegi framework-ul pe care îl folosești. De exemplu în dezvoltarea web folosind MVC, nu prea are ce căuta într-un controller o chestie ce ar sta bine într-un helper/bibliotecă, pentru că atunci când este nevoie să fie apelată bucata respectivă din alt controller, fără refactor, o să fie trist. Desigur, excepție fac acele controllere moștenite, dar și acolo este o linie fină între ce se poate moșteni și ce ar trebui să fie apelabil global.
  • refactor, OOP, clase, interfețe, ‘design pattern’ (exemplu: singleton) ar trebui să nu fie doar cuvinte într-un vocabular de specialitate.

4 thoughts on “Dragă programatorule, dacă îți vine a copy-paste, fă refactor!

  1. SaltwaterC Post author

    Le folosesc pentru interfațarea cu date, gen baze de date, sesiuni, etc. Practic biblioteci ce folosesc drivere ca implementare a unei interfețe sau derivarea unei clase abstracte, drivere ce pot fi schimbate din configurare și oferă un API consistent. Știu că sună a factory, de fapt în măruntaie chiar se asemănă, dar nu permite programatorului să instanțieze aiurea clasele respective, dacă scopul lor este să fie unice (gen o conexiune persistentă către o bază de date).

  2. Mihai Brehar

    E ok sa ai in constructorul unei clase ceva de genul $this->db = Database::getInstance()
    si apoi sa folosesti $this->db->query() in functii.

    Nu e ok sa faci in fiecare functie $db = Database::getInstance()

    De fapt, ar fi de evitat si apelul din constructor. Ar fi bine ca instanta db sa fie data ca parametru la constructor. Evident… sunt cazuri si cazuri, nu poti face la toate asa.

  3. SaltwaterC Post author

    Mă orientez după context. Conform framework-ului MVC pe care încă îl folosesc, operațiile cu date (baza de date, API extern) sunt executate in modele, iar modelul de bază (backbone, înlocuibil) are în constructor un:

    $this->db = Database::instance($this->db);

    unde proprietatea $db se poate seta per model (pentru a mapa un alt grup de conexiune la baza de date). La controllere nu pot instanția clasa folosind un model ca argument, în timp ce nu toate metodele folosesc aceleași metode. De fapt, toate clasele sunt încărcate prin auto-load de către framework, iar cererea de la URL la controller este mapată de către router. Practic nu am acces la parametrii pasați constructorului, deci maxim pot avea un controller de bază ce îl moștenesc. Am câte un constructor de bază pentru layout, AJAX și CLI, fiecare cu particularități (gen în CLI n-am nevoie de sesiune, output buffering sau protecție anti-CSRF/anti-SQLi/anti-XSS, în controller AJAX n-am nevoie de layout XHTML/CSS, etc).

    Mă rog, s-a propus decuplarea bazei de date de model pe forumul comunității de utilizatori ai framework-ului, iar subsemnatul și-a implementat propriul modeler pentru a evita problemele legate de această decuplare, alături de metode pentru CRUD. Îmi place abstractizarea, sau să scriu mai puțin cod, și eventual mai puțin repetitiv.

    Acum poate că mai am îndoieli legate de faptul că toate modelele ce îmi moștenesc modeler-ul sunt implementate ca singleton (Table_Name_Model::instance()->method() folosit de exemplu în controller, bibliotecă, helper sau alt model dacă nu implică join de tabele sau implică baze de date multiple), dar intenția a fost de a evita instanțierea de obiecte model inutile pentru că oricum operațiile la nivel de DB sunt secvențiale, instanțele multiple doar alocă memorie în mod inutil iar câteodată este dificil să impun anumite direcții în dezvoltare fără a apela la restricții precum cea de mai sus. Sunt conștient că design-ul nu este cel mai flexibil, dar înafară de CRUD la nivel de DB, modelerul subsemnatului nu are alt rol. Anumite modificări în interiorul modelului ce presupun alți parametrii de apel către metoda respectivă sau alte date returnate ar presupune aproximativ același proces de refactor în clasele care depind de datele returnate, fie că modelul este declarat ca singleton, fie că nu.

Leave a Reply

Your email address will not be published. Required fields are marked *