CodeGym/Java-blogg/Tilfeldig/Hvordan refaktorisering fungerer i Java
John Squirrels
Nivå
San Francisco

Hvordan refaktorisering fungerer i Java

Publisert i gruppen
Når du lærer å programmere, bruker du mye tid på å skrive kode. De fleste begynnende utviklere tror at det er dette de vil gjøre i fremtiden. Dette er delvis sant, men en programmerers jobb inkluderer også vedlikehold og refaktorisering av kode. I dag skal vi snakke om refaktorering. Hvordan refactoring fungerer i Java - 1

Refaktorering på CodeGym

Refaktorering dekkes to ganger i CodeGym-kurset: Den store oppgaven gir en mulighet til å bli kjent med ekte refactoring gjennom praksis, og leksjonen om refactoring i IDEA hjelper deg å dykke ned i automatiserte verktøy som vil gjøre livet ditt utrolig enklere.

Hva er refaktorering?

Det endrer strukturen til kode uten å endre funksjonaliteten. Anta for eksempel at vi har en metode som sammenligner 2 tall og returnerer sann hvis den første er større og usann ellers:
public boolean max(int a, int b) {
    if(a > b) {
        return true;
    } else if (a == b) {
        return false;
    } else {
        return false;
    }
}
Dette er en ganske uhåndterlig kode. Selv nybegynnere vil sjelden skrive noe slikt, men det er en sjanse. Hvorfor bruke en if-elseblokk hvis du kan skrive 6-linjers metoden mer konsist?
public boolean max(int a, int b) {
     return a > b;
}
Nå har vi en enkel og elegant metode som utfører samme operasjon som eksempelet ovenfor. Dette er hvordan refactoring fungerer: du endrer strukturen til kode uten å påvirke essensen. Det er mange refactoring metoder og teknikker som vi skal se nærmere på.

Hvorfor trenger du refaktorisering?

Det er flere grunner. For eksempel for å oppnå enkelhet og korthet i kode. Tilhengere av denne teorien mener at koden bør være så kortfattet som mulig, selv om det trengs flere dusin linjer med kommentarer for å forstå den. Andre utviklere er overbevist om at koden bør refaktoreres for å gjøre den forståelig med et minimum antall kommentarer. Hvert lag inntar sin egen posisjon, men husk at refaktorering ikke betyr reduksjon . Hovedformålet er å forbedre strukturen til kode. Flere oppgaver kan inkluderes i dette overordnede formålet:
  1. Refaktorering forbedrer forståelsen av kode skrevet av andre utviklere.
  2. Det hjelper å finne og fikse feil.
  3. Det kan akselerere hastigheten på programvareutvikling.
  4. Totalt sett forbedrer det programvaredesign.
Dersom refaktorering ikke utføres over lengre tid, kan utviklingen støte på vanskeligheter, inkludert fullstendig stans i arbeidet.

"Kode lukter"

Når koden krever refactoring, sies det å ha en "lukt". Selvfølgelig ikke bokstavelig talt, men slik kode ser egentlig ikke veldig tiltalende ut. Nedenfor vil vi utforske grunnleggende refactoring-teknikker for den innledende fasen.

Urimelig store klasser og metoder

Klasser og metoder kan være tungvint, umulig å jobbe effektivt med nettopp på grunn av deres enorme størrelse.

Stor klasse

En slik klasse har et stort antall kodelinjer og mange forskjellige metoder. Det er vanligvis lettere for en utvikler å legge til en funksjon i en eksisterende klasse i stedet for å lage en ny, og det er derfor klassen vokser. Som regel er det stappet for mye funksjonalitet inn i en slik klasse. I dette tilfellet hjelper det å flytte en del av funksjonaliteten inn i en egen klasse. Vi vil snakke om dette mer detaljert i avsnittet om refaktoreringsteknikker.

Lang metode

Denne "lukten" oppstår når en utvikler legger til ny funksjonalitet til en metode: "Hvorfor skal jeg sette en parametersjekk i en egen metode hvis jeg kan skrive koden her?", "Hvorfor trenger jeg en egen søkemetode for å finne maksimum element i en matrise? La oss beholde det her. Koden blir klarere på denne måten", og andre slike misoppfatninger.

Det er to regler for refaktorisering av en lang metode:

  1. Hvis du har lyst til å legge til en kommentar når du skriver en metode, bør du legge funksjonaliteten i en egen metode.
  2. Hvis en metode tar mer enn 10-15 linjer med kode, bør du identifisere oppgavene og deloppgavene den utfører og prøve å sette deloppgavene inn i en egen metode.

Det er noen måter å eliminere en lang metode på:

  • Flytt en del av metodens funksjonalitet inn i en egen metode
  • Hvis lokale variabler hindrer deg i å flytte deler av funksjonaliteten, kan du flytte hele objektet til en annen metode.

Bruker mange primitive datatyper

Dette problemet oppstår vanligvis når antall felt i en klasse vokser over tid. For eksempel hvis du lagrer alt (valuta, dato, telefonnumre osv.) i primitive typer eller konstanter i stedet for små objekter. I dette tilfellet vil en god praksis være å flytte en logisk gruppering av felt til en egen klasse (ekstraktklasse). Du kan også legge til metoder til klassen for å behandle dataene.

For mange parametere

Dette er en ganske vanlig feil, spesielt i kombinasjon med en lang metode. Vanligvis oppstår det hvis en metode har for mye funksjonalitet, eller hvis en metode implementerer flere algoritmer. Lange lister med parametere er svært vanskelige å forstå, og det er upraktisk å bruke metoder med slike lister. Som et resultat er det bedre å passere et helt objekt. Hvis et objekt ikke har nok data, bør du bruke et mer generelt objekt eller dele opp metodens funksjonalitet slik at hver metode behandler logisk relaterte data.

Datagrupper

Grupper av logisk relaterte data vises ofte i kode. For eksempel databasetilkoblingsparametere (URL, brukernavn, passord, skjemanavn osv.). Hvis ikke et enkelt felt kan fjernes fra en liste over felt, bør disse feltene flyttes til en egen klasse (ekstraktklasse).

Løsninger som bryter OOP-prinsippene

Disse "luktene" oppstår når en utvikler bryter riktig OOP-design. Dette skjer når han eller hun ikke fullt ut forstår OOP-funksjonene og ikke klarer å bruke dem fullt ut eller riktig.

Manglende bruk av arv

Hvis en underklasse bare bruker en liten undergruppe av funksjonene til overordnet klasse, så lukter det feil hierarki. Når dette skjer, blir vanligvis de overflødige metodene rett og slett ikke overstyrt, eller de gir unntak. En klasse som arver en annen innebærer at barneklassen bruker nesten all funksjonaliteten til foreldreklassen. Eksempel på et korrekt hierarki: Hvordan refactoring fungerer i Java - 2Eksempel på et feil hierarki: Hvordan refactoring fungerer i Java - 3

Bytt uttalelse

Hva kan være galt med et switchutsagn? Det er ille når det blir veldig komplekst. Et relatert problem er et stort antall nestede ifutsagn.

Alternative klasser med ulike grensesnitt

Flere klasser gjør det samme, men metodene deres har forskjellige navn.

Midlertidig felt

Hvis en klasse har et midlertidig felt som et objekt bare trenger av og til når verdien er satt, og det er tomt eller, gud forby, nullresten av tiden, så lukter koden. Dette er en tvilsom designbeslutning.

Lukter som gjør modifikasjon vanskelig

Disse luktene er mer alvorlige. Andre lukter gjør det hovedsakelig vanskeligere å forstå kode, men disse hindrer deg i å endre den. Når du prøver å introdusere nye funksjoner, slutter halvparten av utviklerne, og halvparten blir gale.

Parallelle arvehierarkier

Dette problemet manifesterer seg når underklassing av en klasse krever at du oppretter en annen underklasse for en annen klasse.

Ensartet fordelte avhengigheter

Eventuelle modifikasjoner krever at du ser etter alle klassens bruksområder (avhengigheter) og gjør mange små endringer. Én endring - redigeringer i mange klasser.

Kompleks tre av modifikasjoner

Denne lukten er det motsatte av den forrige: endringer påvirker et stort antall metoder i en klasse. Som regel er slik kode avhengig av kaskade: å endre en metode krever at du fikser noe i en annen, og deretter i den tredje og så videre. Én klasse - mange endringer.

"Søppel lukter"

En ganske ubehagelig kategori av lukt som forårsaker hodepine. Ubrukelig, unødvendig gammel kode. Heldigvis har moderne IDE-er og linters lært å advare mot slike lukter.

Et stort antall kommentarer i en metode

En metode har mange forklarende kommentarer på nesten hver linje. Dette skyldes vanligvis en kompleks algoritme, så det er bedre å dele koden i flere mindre metoder og gi dem forklarende navn.

Duplisert kode

Ulike klasser eller metoder bruker de samme kodeblokkene.

Lat klasse

En klasse tar på seg svært lite funksjonalitet, selv om den var planlagt å være stor.

Ubrukt kode

En klasse, metode eller variabel brukes ikke i koden og er dødvekt.

Overdreven tilkobling

Denne kategorien av lukt er preget av et stort antall uberettigede forhold i koden.

Eksterne metoder

En metode bruker data fra et annet objekt mye oftere enn sine egne data.

Upassende intimitet

En klasse avhenger av implementeringsdetaljene til en annen klasse.

Lange klassesamtaler

En klasse ringer til en annen, som ber om data fra en tredje, som får data fra en fjerde, og så videre. En så lang kjede av samtaler betyr stor avhengighet av dagens klassestruktur.

Task-dealer klasse

En klasse er kun nødvendig for å sende en oppgave til en annen klasse. Kanskje den bør fjernes?

Refaktoreringsteknikker

Nedenfor vil vi diskutere grunnleggende refactoring-teknikker som kan bidra til å eliminere de beskrevne kodeluktene.

Trekk ut en klasse

En klasse utfører for mange funksjoner. Noen av dem må flyttes til en annen klasse. Anta for eksempel at vi har en Humanklasse som også lagrer en hjemmeadresse og har en metode som returnerer hele adressen:
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();
    }
 }
Det er god praksis å sette adresseinformasjonen og den tilhørende metoden (databehandlingsatferd) i en egen klasse:
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();
   }
}

Trekk ut en metode

Hvis en metode har en eller annen funksjonalitet som kan isoleres, bør du plassere den i en egen metode. For eksempel, en metode som beregner røttene til en kvadratisk ligning:
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");
    }
}
Vi beregner hvert av de tre mulige alternativene i separate metoder:
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");
}
Hver metodes kode har blitt mye kortere og lettere å forstå.

Passerer en hel gjenstand

Når en metode kalles med parametere, kan du noen ganger se kode som dette:
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;
}
Den employeeMethodhar 2 hele linjer viet til å motta verdier og lagre dem i primitive variabler. Noen ganger kan slike konstruksjoner ta opptil 10 linjer. Det er mye lettere å sende selve objektet og bruke det til å trekke ut de nødvendige dataene:
public void employeeMethod(Employee employee) {
    // Some actions
    double monthlySalary = getMonthlySalary(employee);
    // Continue processing
}

public double getMonthlySalary(Employee employee) {
    return (employee.getYearlySalary() + employee.getAwards())/12;
}

Enkelt, kort og konsist.

Å gruppere felt logisk og flytte dem inn i separate, classDespitedet faktum at eksemplene ovenfor er veldig enkle, og når du ser på dem, kan mange av dere spørre: "Hvem gjør dette?", mange utviklere gjør slike strukturelle feil på grunn av uforsiktighet, manglende vilje til å refaktorisere koden, eller rett og slett en holdning om "det er bra nok".

Hvorfor refaktorering er effektivt

Som et resultat av god refactoring har et program lettlest kode, utsiktene til å endre logikken er ikke skremmende, og å introdusere nye funksjoner blir ikke kodeanalysehelvete, men er i stedet en hyggelig opplevelse for et par dager . Du bør ikke tenke på om det ville være lettere å skrive et program fra bunnen av. Anta for eksempel at teamet ditt anslår at arbeidskraften som kreves for å forstå, analysere og gjenopprette kode vil være større enn å implementere den samme funksjonaliteten fra bunnen av. Eller hvis koden som skal refaktoreres har mange problemer som er vanskelige å feilsøke. Å vite hvordan man kan forbedre strukturen til kode er viktig i arbeidet til en programmerer. Og å lære å programmere i Java gjøres best på CodeGym, nettkurset som legger vekt på praksis. 1200+ oppgaver med umiddelbar verifisering, omtrent 20 miniprosjekter, spilloppgaver — alt dette vil hjelpe deg å føle deg trygg på koding. Den beste tiden å starte er nå :)

Ressurser for å fordype deg ytterligere i refaktorisering

Den mest kjente boken om refactoring er "Refactoring. Improving the Design of Existing Code" av Martin Fowler. Det er også en interessant publikasjon om refactoring, basert på en tidligere bok: "Refactoring Using Patterns" av Joshua Kerievsky. Apropos mønstre... Ved refaktorisering er det alltid veldig nyttig å kjenne til grunnleggende designmønstre. Disse utmerkede bøkene vil hjelpe med dette: Apropos mønstre... Ved refaktorisering er det alltid veldig nyttig å kjenne til grunnleggende designmønstre. Disse utmerkede bøkene vil hjelpe med dette:
  1. "Design Patterns" av Eric Freeman, Elizabeth Robson, Kathy Sierra og Bert Bates, fra Head First-serien
  2. "The Art of Readable Code" av Dustin Boswell og Trevor Foucher
  3. "Code Complete" av Steve McConnell, som setter prinsippene for vakker og elegant kode.
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå