Iespējams, es rakstu par kaut ko, kas daudziem ir acīmredzams, bet varbūt ne visiem. Manuprāt, refaktorizācija ir sarežģīta tēma, jo tā ietver koda maiņu, neietekmējot tā darbību.
Tāpēc dažiem ir nesaprotami, ka refactoring patiesībā ir programmēšanas joma, un tā ir arī ļoti svarīga programmētāja darba daļa. Kods nepārtraukti attīstās, tas tiks pārveidots, kamēr vien būs iespēja pievienot jaunas funkcionalitātes. Tomēr tas var iegūt tādu formu, kas vairs neļauj efektīvi pievienot jaunas funkcionalitātes, un vieglāk būtu visu programmu pārrakstīt no jauna.
Kas ir refaktorizācija?
Parasti atbilde, ko dzirdat, ir, ka tā ir koda struktūras maiņa, piemērojot virkni refaktorizācijas transformāciju, neietekmējot novērojamo koda uzvedību. Tā ir taisnība. Nesen es saskāros arī ar definīciju, ko sniedz Martins Faulers (Martin Fowler) savā grāmatā “Esošā kodeksa dizaina uzlabošana” kur viņš apraksta refactoring kā “veikt lielas pārmaiņas ar maziem soļiem”. Viņš apraksta refactoring kā izmaiņas kodā, kas neietekmē tā darbību, taču viņš uzsver, ka tas jādara mazos soļos.
Grāmatā arī atbalstīts, ka refactoring neietekmē koda darbību un norāda, ka tas nekādā gadījumā neietekmē testu izpildi. Tajā soli pa solim aprakstīts, kā droši veikt refactoring. Man patika viņa grāmata, jo tajā aprakstīti vienkārši triki, kurus var izmantot ikdienas darbā.
Kāpēc mums ir nepieciešama refaktorizācija?
Visbiežāk tas var būt nepieciešams, ja vēlaties ieviest jaunu funkcionalitāti, bet pašreizējā koda versija to neļauj vai bez izmaiņām kodā tas būtu sarežģītāk. Tāpat tā ir noderīga gadījumos, kad pievienot vairāk funkciju ir neizdevīgi laika ziņā, proti, ātrāk būtu kodu pārrakstīt no jauna. Manuprāt, dažkārt tiek aizmirsts, ka refactoring var padarīt kodu tīrāku un lasāmāku. Martins savā grāmatā raksta, kā viņš veic refaktorizāciju, kad jūt nepatīkamas smakas kodā, un, kā viņš to izsaka, “tas vienmēr atstāj vietu labākam”. Un viņš mani pārsteidza ar to, ka refaktorizāciju uzskata par ikdienas darba ar kodu elementu. Man kodi bieži vien ir nesaprotami, to lasīšana ir neliels pārdzīvojums, jo kods bieži vien ir neintuitīvs.
Labi izstrādātas programmas atšķirīgā iezīme ir tās Modularitāte, pateicoties kam pietiek zināt tikai nelielu daļu koda, lai ieviestu lielāko daļu modifikāciju. Modularitāte arī atvieglo jaunu cilvēku iesaisti un ļauj efektīvāk sākt darbu. Lai panāktu šo modularitāti, saistītajiem programmas elementiem jābūt sagrupētiem kopā, savienojumiem jābūt saprotamiem un viegli atrodamiem. Nav vienotu noteikumu, kā to izdarīt. Zinot un saprotot arvien vairāk un labāk, kā kodam ir jāstrādā, jūs varat sagrupēt elementus, bet dažreiz ir arī jātestē un jāpārbauda.
Viens no refaktorizācijas noteikumiem YAGNI, tas ir akronīms, kas nozīmē ‘Tev tas nebūs vajadzīgs’, un tā izcelsme meklējama eXtreme programmēšana (XP) ko galvenokārt izmanto Agileprogrammatūras izstrāde komandas. Īss stāsts, YAGNI teikts, ka ir jāveic tikai aktuāli darbi. Tas būtībā nozīmē, ka pat tad, ja kaut kas varētu būt nepieciešams nākotnē, to nevajadzētu darīt tieši tagad. Taču mēs arī nevaram sagraut turpmākus paplašinājumus, un tieši šeit modularitāte kļūst svarīga.
Runājot par refactoring, jāmin viens no būtiskākajiem elementiem, proti, testi. In refactoring, mums ir jāzina, ka kods joprojām darbojas, jo refactoring nemaina tā darbību, bet gan struktūru, tāpēc visi testi ir jāiztur. Vislabāk ir pēc katras nelielas pārveidošanas palaist testus tai koda daļai, ar kuru mēs strādājam. Tas dod mums apstiprinājums, ka viss darbojas kā nākas, un tas saīsina visas operācijas laiku. Tieši par to savā grāmatā runā Martins - pēc iespējas biežāk veikt testus, lai nevajadzētu spert soli atpakaļ un tērēt laiku, meklējot transformāciju, kas kaut ko sabojājusi.
Koda refaktorizācija bez testēšanas ir sāpīgi, un pastāv liela iespēja, ka kaut kas būs nepareizi. Ja tas ir iespējams, vislabāk būtu pievienot vismaz dažus pamata testus, kas mums sniegtu nelielu pārliecību, ka kods darbojas.
Turpmāk uzskaitītās transformācijas ir tikai piemēri, taču tās ir ļoti noderīgas ikdienas programmēšanā:
Funkciju un mainīgo izvilkšana - ja funkcija ir pārāk gara, pārbaudiet, vai nav mazāk svarīgu funkciju, ko varētu izvilkt. Tas pats attiecas uz garām rindām. Šīs transformācijas var palīdzēt atrast dublēšanos kodā. Pateicoties mazajām funkcijām, kods kļūst skaidrāks un saprotamāks,
Funkciju un mainīgo pārdēvēšana - pareizas nosaukumu piešķiršanas konvencijas izmantošana ir būtiska labai programmēšanai. Labi izvēlēti mainīgo nosaukumi var daudz ko pastāstīt par kodu,
Funkciju grupēšana vienā klasē - šī izmaiņa ir noderīga, ja divas klases veic līdzīgas darbības, jo tā var saīsināt klases garumu,
Pārrakstošais ieliktais paziņojums - ja nosacījums tiek pārbaudīts īpašam gadījumam, izdodiet atgriešanas paziņojumu, kad nosacījums ir izpildīts. Šāda veida testus bieži dēvē par aizsargteikumu. Ievietotā nosacījuma izteikuma aizstāšana ar izejas izteikumu maina uzsvaru kodā. If-else konstrukcijā abiem variantiem tiek piešķirta vienāda nozīme. Personai, kas lasa kodu, tas ir vēstījums, ka katrs no tiem ir vienlīdz iespējams un svarīgs,
Īpaša gadījuma ieviešana - ja dažus nosacījumus kodā izmantojat vairākkārt, iespējams, ir vērts tiem izveidot atsevišķu struktūru. Rezultātā lielāko daļu īpašo gadījumu pārbaužu var aizstāt ar vienkāršiem funkciju izsaukumiem. Bieži vien parastā vērtība, kurai nepieciešama īpaša apstrāde, ir nulle. Tāpēc šo modeli bieži sauc par nulles objektu. Tomēr šo pieeju var izmantot jebkurā īpašā gadījumā,
Nosacījuma instrukcijas polimorfisma aizstāšana.
Piemērs
Šis ir raksts par refactoring un ir nepieciešams piemērs. Turpmāk es vēlos parādīt vienkāršu refaktorizācijas paraugu, izmantojot Iestrādātā izteikuma pārrakstīšana un Nosacījuma instrukcijas polimorfisma aizstāšana. Pieņemsim, ka mums ir programmas funkcija, kas atgriež a hash ar informāciju par to, kā laistīt augus reālajā dzīvē. Šāda informācija, iespējams, būtu iekļauta modelī, bet šajā piemērā tā ir iekļauta funkcijā.
def laistīšanas_info(augs)
result = {}
if plant.is_a? Suculent || plant.is_a? Kaktuss
result = { water_amount: " , how_to: "No apakšas", laistīšanas ilgums: "2 nedēļas" }
elsif plant.is_a? Alocasia || plant.is_a? Maranta
result = { water_amount: "Liels daudzums", how_to: laistīšanas_ilgums: "Kā vēlaties", laistīšanas_ilgums: "5 dienas" }
elsif plant.is_a? Peperomia
result = { water_amount: "Dicent amount",
how_to: "No apakšas! tām nepatīk ūdens uz lapām",
laistīšanas ilgums: "1 nedēļa" }
citādi
result = { water_amount: "Dicent amount",
how_to: "Kā vēlaties",
laistīšanas ilgums: "1 nedēļa"
}
beigas
return result
beigas
Ideja ir mainīt, ja atgriezties:
ja plant.isa? Suculent || plant.isa? Cactus
result = { wateramount: "A little bit " , howto: "No apakšas",
Uz
return { water_amount: " , how_to: "No apakšas",laistīšanas_ilgums: "2 nedēļas" } if plant.is_a? Suculent || plant.is_a? Kaktuss
atgriezties { ūdenssumma: “Nedaudz” , kāuz: “No apakšas”, laistīšanailgums: “2 nedēļas” } if plant.isa? Sukulents || plant.is_a? Kaktuss
Un tā tālāk, līdz nonākam pie funkcijas, kas izskatās šādi:
def laistīšanas_info(augs)
return result = { wateramount: " , howto: "No apakšas", wateringduration: "2 nedēļas" } if plant.isa? Suculent || plant.is_a? Cactus
return result = { wateramount: "Liels daudzums", howto: "Kā vēlaties", wateringduration: "5 dienas" } if plant.isa? Alocasia || plant.is_a? Maranta
return result = { water_amount: "Dicent amount",
howto: "No apakšas! viņiem nepatīk ūdens uz lapām",
wateringduration: "1 nedēļa" } if plant.is_a? Peperomia
return result = { water_amount: "Dicent amount",
how_to: "Kā vēlaties",
laistīšanas ilgums: "1 nedēļa"
}
beigas
Pašās beigās mums jau bija atgriešanās rezultāts. Labs ieradums ir to darīt soli pa solim un pārbaudīt katru izmaiņu. Jūs varētu aizstāt šo if bloku ar komutatora gadījumu, un tas uzreiz izskatītos labāk, un jums nebūtu katru reizi jāpārbauda visi if. Tas izskatītos šādi:
def laistīšanas_info(augs)
swich plant.class.to_string
case sukulents, kaktuss
{ wateramount: "A little bit " , howto: "No apakšas", laistīšanas ilgums: "2 nedēļas" }
gadījums Alocasia, Maranta
{ wateramount: "Liels daudzums", howto: laistīšanas ilgums: "Kā vēlaties", laistīšanas ilgums: "5 dienas" }
gadījums Peperomia
{ water_amount: "Dicent amount",
how_to: "No apakšas! tiem nepatīk ūdens uz lapām",
laistīšanas ilgums: "1 nedēļa" }
citādi
{ water_amount: "Dicent amount",
how_to: "Kā vēlaties",
laistīšanas ilgums: "1 nedēļa” }
beigas
beigas
Un pēc tam varat izmantot Nosacījuma instrukcijas polimorfisma aizstāšana. Tas ir, lai izveidotu klasi ar funkciju, kas atgriež pareizo vērtību un pārslēdz tās pareizajās vietās.
Un galvenajā laistīšanas_infofunkcijā kods izskatīsies šādi:
def laistīšanas_info(augs)
plant.map(&:watering_info)
end
Protams, šo funkciju var noņemt un aizstāt ar tās saturu. Ar šo piemēru es gribēju parādīt vispārējo refaktorizācijas modelis.
Kopsavilkums
Pārstrādāšana ir liels temats. Es ceru, ka šis raksts bija pamudinājums lasīt vairāk. Šie refaktorizēšanas prasmes palīdzēs jums noķert kļūdas un uzlabot tīra koda darbnīcu. Es iesaku izlasīt Martina grāmatu (Improving the Design of Existing Code), kas ir diezgan vienkāršs un noderīgs noteikumu kopums par to, kā uzlabot esošo kodu. refactoring. Autors soli pa solim rāda dažādas transformācijas ar pilnu skaidrojumu un motivāciju, kā arī padomus, kā izvairīties no kļūdām, kas tiek darītas, lai. refactoring. Pateicoties tās daudzpusībai, tā ir lieliska grāmata gan frontend, gan backend izstrādātāji.