CodeGym /Java Blog /Random /Paano gumagana ang refactoring sa Java
John Squirrels
Antas
San Francisco

Paano gumagana ang refactoring sa Java

Nai-publish sa grupo
Habang natututo ka kung paano magprogram, gumugugol ka ng maraming oras sa pagsusulat ng code. Karamihan sa mga nagsisimulang developer ay naniniwala na ito ang kanilang gagawin sa hinaharap. Ito ay bahagyang totoo, ngunit kasama rin sa trabaho ng isang programmer ang pagpapanatili at pag-refactor ng code. Ngayon ay pag-uusapan natin ang tungkol sa refactoring. Paano gumagana ang refactoring sa Java - 1

Refactoring sa CodeGym

Ang refactoring ay sinasaklaw ng dalawang beses sa kursong CodeGym: Ang malaking gawain ay nagbibigay ng pagkakataong maging pamilyar sa tunay na refactoring sa pamamagitan ng pagsasanay, at ang aralin sa refactoring sa IDEA ay tumutulong sa iyong sumisid sa mga automated na tool na magpapadali sa iyong buhay.

Ano ang refactoring?

Binabago nito ang istruktura ng code nang hindi binabago ang pag-andar nito. Halimbawa, ipagpalagay na mayroon kaming isang paraan na naghahambing ng 2 numero at nagbabalik ng true kung ang una ay mas malaki at mali kung hindi:

    public boolean max(int a, int b) {
        if(a > b) {
            return true;
        } else if (a == b) {
            return false;
        } else {
            return false;
        }
    }
Ito ay isang medyo mahirap gamitin na code. Kahit na ang mga baguhan ay bihirang magsulat ng ganito, ngunit may pagkakataon. Bakit gagamit ng if-elseblock kung maaari mong isulat ang 6-line na paraan nang mas maigsi?

 public boolean max(int a, int b) {
      return a > b;
 }
Ngayon ay mayroon na kaming simple at eleganteng pamamaraan na gumaganap ng parehong operasyon tulad ng halimbawa sa itaas. Ito ay kung paano gumagana ang refactoring: binabago mo ang istraktura ng code nang hindi naaapektuhan ang kakanyahan nito. Maraming mga paraan at pamamaraan ng refactoring na susuriin natin nang mas malapitan.

Bakit kailangan mo ng refactoring?

Mayroong ilang mga dahilan. Halimbawa, upang makamit ang pagiging simple at kaiklian sa code. Ang mga tagapagtaguyod ng teoryang ito ay naniniwala na ang code ay dapat na maigsi hangga't maaari, kahit na ilang dosenang linya ng mga komento ang kailangan upang maunawaan ito. Ang iba pang mga developer ay kumbinsido na ang code ay dapat na refactored upang gawin itong maliwanag na may pinakamababang bilang ng mga komento. Ang bawat koponan ay gumagamit ng sarili nitong posisyon, ngunit tandaan na ang refactoring ay hindi nangangahulugan ng pagbabawas . Ang pangunahing layunin nito ay upang mapabuti ang istraktura ng code. Maaaring isama ang ilang gawain sa pangkalahatang layuning ito:
  1. Pinapabuti ng refactoring ang pag-unawa sa code na isinulat ng ibang mga developer.
  2. Nakakatulong ito sa paghahanap at pag-aayos ng mga bug.
  3. Maaari nitong mapabilis ang bilis ng pagbuo ng software.
  4. Sa pangkalahatan, pinapabuti nito ang disenyo ng software.
Kung ang refactoring ay hindi ginanap sa loob ng mahabang panahon, ang pag-unlad ay maaaring makatagpo ng mga paghihirap, kabilang ang isang kumpletong paghinto sa trabaho.

"Ang amoy ng code"

Kapag ang code ay nangangailangan ng refactoring, ito ay sinasabing may "amoy". Siyempre, hindi literal, ngunit ang naturang code ay talagang hindi mukhang nakakaakit. Sa ibaba ay tutuklasin natin ang mga pangunahing pamamaraan ng refactoring para sa paunang yugto.

Hindi makatwirang malalaking klase at pamamaraan

Ang mga klase at pamamaraan ay maaaring maging mahirap, imposibleng gumana nang epektibo nang tumpak dahil sa kanilang malaking sukat.

Malaking klase

Ang nasabing klase ay may malaking bilang ng mga linya ng code at maraming iba't ibang pamamaraan. Karaniwang mas madali para sa isang developer na magdagdag ng feature sa isang umiiral nang klase kaysa gumawa ng bago, kaya naman lumalaki ang klase. Bilang isang patakaran, masyadong maraming pag-andar ang napuno sa gayong klase. Sa kasong ito, nakakatulong na ilipat ang bahagi ng functionality sa isang hiwalay na klase. Pag-uusapan natin ito nang mas detalyado sa seksyon sa mga diskarte sa refactoring.

Mahabang pamamaraan

Ang "amoy" na ito ay lumalabas kapag nagdagdag ang isang developer ng bagong functionality sa isang paraan: "Bakit ko dapat ilagay ang isang parameter check sa isang hiwalay na paraan kung maaari kong isulat ang code dito?", "Bakit kailangan ko ng isang hiwalay na paraan ng paghahanap upang mahanap ang maximum elemento sa isang array? Itago natin ito dito. Magiging mas malinaw ang code sa ganitong paraan", at iba pang maling akala.

Mayroong dalawang mga patakaran para sa refactoring ng isang mahabang paraan:

  1. Kung gusto mong magdagdag ng komento kapag nagsusulat ng paraan, dapat mong ilagay ang functionality sa isang hiwalay na paraan.
  2. Kung ang isang pamamaraan ay tumatagal ng higit sa 10-15 linya ng code, dapat mong tukuyin ang mga gawain at subtask na ginagawa nito at subukang ilagay ang mga subtask sa isang hiwalay na paraan.

Mayroong ilang mga paraan upang maalis ang isang mahabang paraan:

  • Ilipat ang bahagi ng functionality ng pamamaraan sa isang hiwalay na paraan
  • Kung pinipigilan ka ng mga lokal na variable na ilipat ang bahagi ng functionality, maaari mong ilipat ang buong object sa ibang paraan.

Gumagamit ng maraming primitive na uri ng data

Ang problemang ito ay karaniwang nangyayari kapag ang bilang ng mga patlang sa isang klase ay lumalaki sa paglipas ng panahon. Halimbawa, kung iimbak mo ang lahat (pera, petsa, numero ng telepono, atbp.) sa mga primitive na uri o constant sa halip na maliliit na bagay. Sa kasong ito, ang isang magandang kasanayan ay ang paglipat ng isang lohikal na pagpapangkat ng mga patlang sa isang hiwalay na klase (extract na klase). Maaari ka ring magdagdag ng mga pamamaraan sa klase upang maproseso ang data.

Masyadong maraming mga parameter

Ito ay isang medyo karaniwang pagkakamali, lalo na sa kumbinasyon ng isang mahabang paraan. Karaniwan, ito ay nangyayari kung ang isang pamamaraan ay may masyadong maraming pag-andar, o kung ang isang pamamaraan ay nagpapatupad ng maraming mga algorithm. Napakahirap maunawaan ang mahabang listahan ng mga parameter, at hindi maginhawa ang paggamit ng mga pamamaraan na may ganitong mga listahan. Bilang resulta, mas mahusay na ipasa ang isang buong bagay. Kung walang sapat na data ang isang bagay, dapat kang gumamit ng mas pangkalahatang bagay o hatiin ang functionality ng pamamaraan upang ang bawat pamamaraan ay magproseso ng lohikal na nauugnay na data.

Mga pangkat ng data

Ang mga pangkat ng lohikal na nauugnay na data ay madalas na lumalabas sa code. Halimbawa, ang mga parameter ng koneksyon sa database (URL, username, password, pangalan ng schema, atbp.). Kung walang isang field ang maaaring alisin mula sa isang listahan ng mga field, ang mga field na ito ay dapat ilipat sa isang hiwalay na klase (extract class).

Mga solusyon na lumalabag sa mga prinsipyo ng OOP

Ang mga "amoy" na ito ay nangyayari kapag ang isang developer ay lumabag sa wastong disenyo ng OOP. Nangyayari ito kapag hindi niya lubos na nauunawaan ang mga kakayahan ng OOP at nabigo siyang ganap o maayos na gamitin ang mga ito.

Pagkabigong gumamit ng mana

Kung ang isang subclass ay gumagamit lamang ng isang maliit na subset ng mga function ng parent class, kung gayon ito ay amoy ng maling hierarchy. Kapag nangyari ito, kadalasan ang mga kalabisan na pamamaraan ay hindi na-override o nagtatapon sila ng mga pagbubukod. Ipinahihiwatig ng isang klase na nagmana ng isa pa na ginagamit ng child class ang halos lahat ng functionality ng parent class. Halimbawa ng tamang hierarchy: Paano gumagana ang refactoring sa Java - 2Halimbawa ng maling hierarchy: Paano gumagana ang refactoring sa Java - 3

Palitan ang pahayag

Ano ang maaaring mali sa isang switchpahayag? Ito ay masama kapag ito ay nagiging napaka-kumplikado. Ang isang kaugnay na problema ay isang malaking bilang ng mga nested ifna pahayag.

Mga alternatibong klase na may iba't ibang interface

Ginagawa ng maramihang mga klase ang parehong bagay, ngunit ang kanilang mga pamamaraan ay may iba't ibang mga pangalan.

Pansamantalang larangan

Kung ang isang klase ay may pansamantalang field na kailangan ng isang bagay paminsan-minsan lamang kapag ang halaga nito ay nakatakda, at ito ay walang laman o, ipinagbawal ng Diyos, nullsa natitirang oras, kung gayon ang code ay amoy. Ito ay isang kaduda-dudang desisyon sa disenyo.

Mga amoy na nagpapahirap sa pagbabago

Ang mga amoy na ito ay mas seryoso. Ang iba pang mga amoy ay pangunahing nagpapahirap sa pag-unawa sa code, ngunit pinipigilan ka nitong baguhin ito. Kapag sinubukan mong ipakilala ang anumang mga bagong feature, huminto ang kalahati ng mga developer, at nabaliw ang kalahati.

Parallel inheritance hierarchies

Ang problemang ito ay nagpapakita mismo kapag ang pag-subclass ng isang klase ay nangangailangan sa iyo na lumikha ng isa pang subclass para sa ibang klase.

Uniformly distributed dependencies

Ang anumang mga pagbabago ay nangangailangan sa iyo na hanapin ang lahat ng mga gamit ng isang klase (dependencies) at gumawa ng maraming maliliit na pagbabago. Isang pagbabago — mga pag-edit sa maraming klase.

Kumplikadong puno ng mga pagbabago

Ang amoy na ito ay kabaligtaran ng nauna: ang mga pagbabago ay nakakaapekto sa isang malaking bilang ng mga pamamaraan sa isang klase. Bilang isang patakaran, ang naturang code ay may cascading dependence: ang pagbabago ng isang paraan ay nangangailangan sa iyo na ayusin ang isang bagay sa isa pa, at pagkatapos ay sa pangatlo at iba pa. Isang klase — maraming pagbabago.

"Amoy basura"

Isang medyo hindi kasiya-siyang kategorya ng mga amoy na nagdudulot ng pananakit ng ulo. Walang silbi, hindi kailangan, lumang code. Sa kabutihang palad, ang mga modernong IDE at linter ay natutong magbigay ng babala sa gayong mga amoy.

Ang isang malaking bilang ng mga komento sa isang paraan

Ang isang pamamaraan ay may maraming mga paliwanag na komento sa halos bawat linya. Kadalasan ito ay dahil sa isang kumplikadong algorithm, kaya mas mainam na hatiin ang code sa ilang mas maliliit na pamamaraan at bigyan sila ng mga paliwanag na pangalan.

Dobleng code

Ang iba't ibang klase o pamamaraan ay gumagamit ng parehong mga bloke ng code.

Tamad na klase

Ang isang klase ay tumatagal ng napakakaunting functionality, kahit na ito ay binalak na maging malaki.

Hindi nagamit na code

Ang isang klase, pamamaraan o variable ay hindi ginagamit sa code at ito ay patay na timbang.

Sobrang connectivity

Ang kategoryang ito ng mga amoy ay nailalarawan sa pamamagitan ng isang malaking bilang ng mga hindi makatarungang relasyon sa code.

Panlabas na pamamaraan

Ang isang pamamaraan ay gumagamit ng data mula sa isa pang bagay nang mas madalas kaysa sa sarili nitong data.

Hindi nararapat na intimacy

Ang isang klase ay nakasalalay sa mga detalye ng pagpapatupad ng isa pang klase.

Mahabang tawag sa klase

Ang isang klase ay tumatawag sa isa pa, na humihiling ng data mula sa isang pangatlo, na nakakakuha ng data mula sa isang ikaapat, at iba pa. Ang ganitong mahabang hanay ng mga tawag ay nangangahulugan ng mataas na pag-asa sa kasalukuyang istruktura ng klase.

Klase ng task-dealer

Ang isang klase ay kailangan lamang para sa pagpapadala ng gawain sa ibang klase. Siguro dapat itong alisin?

Mga diskarte sa refactoring

Sa ibaba ay tatalakayin natin ang mga pangunahing pamamaraan ng refactoring na makakatulong sa pag-alis ng inilarawang mga amoy ng code.

I-extract ang isang klase

Ang isang klase ay gumaganap ng masyadong maraming mga function. Ang ilan sa kanila ay dapat ilipat sa ibang klase. Halimbawa, ipagpalagay na mayroon kaming isang Humanklase na nag-iimbak din ng isang address ng tahanan at may isang paraan na nagbabalik ng buong address:

class Human {
    private String name;
    private String age;
    private String country;
    private String city;
    private String street;
    private String house;
    private String quarter;
 
    public String getFullAddress() {
        StringBuilder result = new StringBuilder();
        return result
                        .append(country)
                        .append(", ")
                        .append(city)
                        .append(", ")
                        .append(street)
                        .append(", ")
                        .append(house)
                        .append(" ")
                        .append(quarter).toString();
    }
 }
Magandang kasanayan na ilagay ang impormasyon ng address at nauugnay na pamamaraan (pag-uugali sa pagproseso ng data) sa isang hiwalay na klase:

 class Human {
    private String name;
    private String age;
    private Address address;
 
    private String getFullAddress() {
        return address.getFullAddress();
    }
 }
 class Address {
    private String country;
    private String city;
    private String street;
    private String house;
    private String quarter;
 
    public String getFullAddress() {
        StringBuilder result = new StringBuilder();
        return result
                        .append(country)
                        .append(", ")
                        .append(city)
                        .append(", ")
                        .append(street)
                        .append(", ")
                        .append(house)
                        .append(" ")
                        .append(quarter).toString();
    }
 }

I-extract ang isang paraan

Kung ang isang paraan ay may ilang functionality na maaaring ihiwalay, dapat mong ilagay ito sa isang hiwalay na paraan. Halimbawa, isang paraan na kinakalkula ang mga ugat ng isang quadratic equation:

    public void calcQuadraticEq(double a, double b, double c) {
        double D = b * b - 4 * a * c;
        if (D > 0) {
            double x1, x2;
            x1 = (-b - Math.sqrt(D)) / (2 * a);
            x2 = (-b + Math.sqrt(D)) / (2 * a);
            System.out.println("x1 = " + x1 + ", x2 = " + x2);
        }
        else if (D == 0) {
            double x;
            x = -b / (2 * a);
            System.out.println("x = " + x);
        }
        else {
            System.out.println("Equation has no roots");
        }
    }
Kinakalkula namin ang bawat isa sa tatlong posibleng opsyon sa magkakahiwalay na pamamaraan:

    public void calcQuadraticEq(double a, double b, double c) {
        double D = b * b - 4 * a * c;
        if (D > 0) {
            dGreaterThanZero(a, b, D);
        }
        else if (D == 0) {
            dEqualsZero(a, b);
        }
        else {
            dLessThanZero();
        }
    }
 
    public void dGreaterThanZero(double a, double b, double D) {
        double x1, x2;
        x1 = (-b - Math.sqrt(D)) / (2 * a);
        x2 = (-b + Math.sqrt(D)) / (2 * a);
        System.out.println("x1 = " + x1 + ", x2 = " + x2);
    }
 
    public void dEqualsZero(double a, double b) {
        double x;
        x = -b / (2 * a);
        System.out.println("x = " + x);
    }
 
    public void dLessThanZero() {
        System.out.println("Equation has no roots");
    }
Ang code ng bawat pamamaraan ay naging mas maikli at mas madaling maunawaan.

Pagpasa ng isang buong bagay

Kapag ang isang pamamaraan ay tinawag na may mga parameter, maaari mong makita kung minsan ang code na tulad nito:

 public void employeeMethod(Employee employee) {
     // Some actions
     double yearlySalary = employee.getYearlySalary();
     double awards = employee.getAwards();
     double monthlySalary = getMonthlySalary(yearlySalary, awards);
     // Continue processing
 }
 
 public double getMonthlySalary(double yearlySalary, double awards) {
      return (yearlySalary + awards)/12;
 }
Ang employeeMethoday may 2 buong linya na nakatuon sa pagtanggap ng mga halaga at pag-iimbak ng mga ito sa mga primitive na variable. Minsan ang mga naturang construct ay maaaring tumagal ng hanggang 10 linya. Mas madaling ipasa ang mismong bagay at gamitin ito upang kunin ang kinakailangang data:

 public void employeeMethod(Employee employee) {
     // Some actions
     double monthlySalary = getMonthlySalary(employee);
     // Continue processing
 }
 
 public double getMonthlySalary(Employee employee) {
     return (employee.getYearlySalary() + employee.getAwards())/12;
 }

Simple, maikli, at maigsi.

Logically pagpapangkat-pangkat ng mga patlang at paglipat ng mga ito sa isang hiwalay classDespitena katotohanan na ang mga halimbawa sa itaas ay napaka-simple, at kapag tiningnan mo ang mga ito, marami sa inyo ang maaaring magtanong, "Sino ang gumagawa nito?", maraming mga developer ang gumagawa ng gayong mga pagkakamali sa istruktura dahil sa kawalang-ingat, hindi pagpayag na i-refactor ang code, o simpleng saloobin ng "tama na iyon".

Bakit epektibo ang refactoring

Bilang resulta ng mahusay na refactoring, ang isang programa ay may madaling basahin na code, ang posibilidad na baguhin ang lohika nito ay hindi nakakatakot, at ang pagpapakilala ng mga bagong feature ay hindi nagiging code analysis hell, ngunit ito ay isang magandang karanasan sa loob ng ilang araw. . Hindi mo dapat refactor kung mas madaling magsulat ng isang programa mula sa simula. Halimbawa, ipagpalagay na ang iyong koponan ay tinatantya na ang paggawa na kinakailangan upang maunawaan, suriin, at refactor code ay mas malaki kaysa sa pagpapatupad ng parehong functionality mula sa simula. O kung ang code na ire-refactor ay maraming problema na mahirap i-debug. Ang pag-alam kung paano pagbutihin ang istruktura ng code ay mahalaga sa gawain ng isang programmer. At ang pag-aaral na magprograma sa Java ay pinakamahusay na ginagawa sa CodeGym, ang online na kurso na nagbibigay-diin sa pagsasanay. 1200+ gawain na may agarang pag-verify, mga 20 mini-proyekto, mga gawain sa laro — lahat ng ito ay makakatulong sa iyong kumpiyansa sa coding. Ang pinakamagandang oras para magsimula ay ngayon :)

Mga mapagkukunan upang higit pang isawsaw ang iyong sarili sa refactoring

Ang pinakasikat na libro sa refactoring ay "Refactoring. Pagpapabuti ng Disenyo ng Umiiral na Code" ni Martin Fowler. Mayroon ding isang kawili-wiling publikasyon tungkol sa refactoring, batay sa isang nakaraang libro: "Refactoring Using Patterns" ni Joshua Kerievsky. Sa pagsasalita tungkol sa mga pattern... Kapag nagre-refactor, palaging napaka-kapaki-pakinabang na malaman ang mga pangunahing pattern ng disenyo. Ang mahuhusay na aklat na ito ay makakatulong dito: Sa pagsasalita tungkol sa mga pattern... Kapag nagre-refactor, palaging napaka-kapaki-pakinabang na malaman ang mga pangunahing pattern ng disenyo. Ang mga mahuhusay na aklat na ito ay makakatulong dito:
  1. "Mga Pattern ng Disenyo" ni Eric Freeman, Elizabeth Robson, Kathy Sierra, at Bert Bates, mula sa serye ng Head First
  2. "The Art of Readable Code" nina Dustin Boswell at Trevor Foucher
  3. "Code Complete" ni Steve McConnell, na nagtatakda ng mga prinsipyo para sa maganda at eleganteng code.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION