CodeGym /Java blog /Tilfældig /Sådan fungerer refactoring i Java
John Squirrels
Niveau
San Francisco

Sådan fungerer refactoring i Java

Udgivet i gruppen
Når du lærer at programmere, bruger du meget tid på at skrive kode. De fleste begyndende udviklere tror, ​​at det er det, de vil gøre i fremtiden. Dette er til dels rigtigt, men en programmørs job omfatter også vedligeholdelse og refaktorisering af kode. I dag skal vi tale om refactoring. Sådan fungerer refactoring i Java - 1

Refaktorering på CodeGym

Refactoring gennemgås to gange i CodeGym-kurset: Den store opgave giver mulighed for at stifte bekendtskab med ægte refactoring gennem praksis, og lektionen om refactoring i IDEA hjælper dig med at dykke ned i automatiserede værktøjer, der vil gøre dit liv utroligt nemmere.

Hvad er refactoring?

Det ændrer kodestrukturen uden at ændre dens funktionalitet. Antag for eksempel, at vi har en metode, der sammenligner 2 tal og returnerer sand , hvis den første er større og falsk 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 ret uhåndterlig kode. Selv begyndere ville sjældent skrive noget som dette, men der er en chance. Hvorfor bruge en if-elseblok, hvis du kan skrive 6-linjers metoden mere kortfattet?

 public boolean max(int a, int b) {
      return a > b;
 }
Nu har vi en enkel og elegant metode, der udfører samme operation som eksemplet ovenfor. Sådan fungerer refactoring: du ændrer kodens struktur uden at påvirke dens essens. Der er mange refactoring metoder og teknikker, som vi vil se nærmere på.

Hvorfor har du brug for refactoring?

Der er flere årsager. For eksempel for at opnå enkelhed og korthed i kode. Tilhængere af denne teori mener, at kode skal være så kortfattet som muligt, selvom der er brug for flere dusin linjer af kommentarer for at forstå den. Andre udviklere er overbeviste om, at koden bør omstruktureres for at gøre den forståelig med det mindste antal kommentarer. Hvert hold indtager sin egen holdning, men husk, at refaktorering ikke betyder reduktion . Dens hovedformål er at forbedre strukturen af ​​kode. Flere opgaver kan indgå i dette overordnede formål:
  1. Refactoring forbedrer forståelsen af ​​kode skrevet af andre udviklere.
  2. Det hjælper med at finde og rette fejl.
  3. Det kan accelerere hastigheden af ​​softwareudvikling.
  4. Samlet set forbedrer det softwaredesign.
Hvis refactoring ikke udføres i længere tid, kan udviklingen støde på vanskeligheder, herunder fuldstændig standsning af arbejdet.

"Kode lugter"

Når koden kræver refactoring, siges den at have en "lugt". Selvfølgelig ikke bogstaveligt, men sådan en kode ser virkelig ikke særlig tiltalende ud. Nedenfor vil vi udforske grundlæggende refactoring-teknikker til den indledende fase.

Urimeligt store klasser og metoder

Klasser og metoder kan være besværlige, umulige at arbejde effektivt med netop på grund af deres enorme størrelse.

Stor klasse

Sådan en klasse har et stort antal linjer kode og mange forskellige metoder. Det er normalt nemmere for en udvikler at tilføje en funktion til en eksisterende klasse i stedet for at oprette en ny, hvilket er grunden til, at klassen vokser. Som regel er der proppet for meget funktionalitet i sådan en klasse. I dette tilfælde hjælper det at flytte en del af funktionaliteten ind i en separat klasse. Vi vil tale om dette mere detaljeret i afsnittet om refactoring-teknikker.

Lang metode

Denne "lugt" opstår, når en udvikler tilføjer ny funktionalitet til en metode: "Hvorfor skal jeg sætte et parametertjek i en separat metode, hvis jeg kan skrive koden her?", "Hvorfor har jeg brug for en separat søgemetode for at finde det maksimale element i et array? Lad os beholde det her. Koden bliver klarere på denne måde", og andre sådanne misforståelser.

Der er to regler for refaktorisering af en lang metode:

  1. Hvis du har lyst til at tilføje en kommentar, når du skriver en metode, bør du lægge funktionaliteten i en separat metode.
  2. Hvis en metode tager mere end 10-15 linjer kode, bør du identificere de opgaver og underopgaver, den udfører, og forsøge at sætte underopgaverne ind i en separat metode.

Der er et par måder at fjerne en lang metode på:

  • Flyt en del af metodens funktionalitet til en separat metode
  • Hvis lokale variabler forhindrer dig i at flytte en del af funktionaliteten, kan du flytte hele objektet til en anden metode.

Bruger en masse primitive datatyper

Dette problem opstår typisk, når antallet af felter i en klasse vokser over tid. For eksempel hvis du gemmer alt (valuta, dato, telefonnumre osv.) i primitive typer eller konstanter i stedet for små objekter. I dette tilfælde ville en god praksis være at flytte en logisk gruppering af felter til en separat klasse (udtræksklasse). Du kan også tilføje metoder til klassen for at behandle dataene.

For mange parametre

Dette er en ret almindelig fejl, især i kombination med en lang metode. Normalt opstår det, hvis en metode har for meget funktionalitet, eller hvis en metode implementerer flere algoritmer. Lange lister over parametre er meget svære at forstå, og det er ubelejligt at bruge metoder med sådanne lister. Som et resultat er det bedre at passere et helt objekt. Hvis et objekt ikke har nok data, bør du bruge et mere generelt objekt eller opdele metodens funktionalitet, så hver metode behandler logisk relaterede data.

Grupper af data

Grupper af logisk relaterede data vises ofte i kode. For eksempel databaseforbindelsesparametre (URL, brugernavn, adgangskode, skemanavn osv.). Hvis ikke et enkelt felt kan fjernes fra en liste over felter, skal disse felter flyttes til en separat klasse (udtræksklasse).

Løsninger, der overtræder OOP-principperne

Disse "lugte" opstår, når en udvikler overtræder korrekt OOP-design. Dette sker, når han eller hun ikke fuldt ud forstår OOP-kapaciteter og undlader at bruge dem fuldt ud eller korrekt.

Manglende brug af arv

Hvis en underklasse kun bruger en lille undergruppe af forældreklassens funktioner, så lugter det af det forkerte hierarki. Når dette sker, tilsidesættes normalt de overflødige metoder simpelthen ikke, eller de kaster undtagelser. En klasse, der arver en anden, indebærer, at den underordnede klasse bruger næsten al den overordnede klasses funktionalitet. Eksempel på et korrekt hierarki: Sådan fungerer refactoring i Java - 2Eksempel på et forkert hierarki: Sådan fungerer refactoring i Java - 3

Skift erklæring

Hvad kan der være galt med et switchudsagn? Det er slemt, når det bliver meget komplekst. Et relateret problem er et stort antal indlejrede ifudsagn.

Alternative klasser med forskellige grænseflader

Flere klasser gør det samme, men deres metoder har forskellige navne.

Midlertidigt felt

Hvis en klasse har et midlertidigt felt, som et objekt kun har brug for lejlighedsvis, når dets værdi er sat, og det er tomt eller, gud forbyde, nullresten af ​​tiden, så lugter koden. Dette er en tvivlsom designbeslutning.

Lugte, der gør modifikation vanskelig

Disse lugte er mere alvorlige. Andre lugte gør det primært sværere at forstå kode, men disse forhindrer dig i at ændre den. Når du prøver at introducere nye funktioner, stopper halvdelen af ​​udviklerne, og halvdelen går amok.

Parallelle arvehierarkier

Dette problem manifesterer sig, når underklasser af en klasse kræver, at du opretter en anden underklasse til en anden klasse.

Ensartet fordelte afhængigheder

Eventuelle ændringer kræver, at du kigger efter alle en klasses anvendelser (afhængigheder) og foretager en masse små ændringer. Én ændring — redigeringer i mange klasser.

Kompleks træ af ændringer

Denne lugt er det modsatte af den forrige: ændringer påvirker et stort antal metoder i en klasse. Som regel har en sådan kode kaskadeafhængighed: Ændring af en metode kræver, at du reparerer noget i en anden, og derefter i den tredje og så videre. Én klasse - mange ændringer.

"Affald lugter"

En ret ubehagelig kategori af lugte, der forårsager hovedpine. Ubrugelig, unødvendig gammel kode. Heldigvis har moderne IDE'er og linters lært at advare mod sådanne lugte.

Et stort antal kommentarer i en metode

En metode har en masse forklarende kommentarer på næsten hver linje. Dette skyldes normalt en kompleks algoritme, så det er bedre at dele koden op i flere mindre metoder og give dem forklarende navne.

Duplikeret kode

Forskellige klasser eller metoder bruger de samme kodeblokke.

Dovne klasse

En klasse påtager sig meget lidt funktionalitet, selvom den var planlagt til at være stor.

Ubrugt kode

En klasse, metode eller variabel bruges ikke i koden og er dødvægt.

Overdreven tilslutning

Denne kategori af lugte er karakteriseret ved et stort antal uberettigede forhold i koden.

Eksterne metoder

En metode bruger data fra et andet objekt meget oftere end sine egne data.

Upassende intimitet

En klasse afhænger af implementeringsdetaljerne for en anden klasse.

Lange klasseopkald

En klasse ringer til en anden, som anmoder om data fra en tredje, som får data fra en fjerde, og så videre. En så lang kæde af opkald betyder stor afhængighed af den nuværende klassestruktur.

Task-dealer klasse

En klasse er kun nødvendig for at sende en opgave til en anden klasse. Måske skal den fjernes?

Refaktoreringsteknikker

Nedenfor vil vi diskutere grundlæggende refactoring-teknikker, der kan hjælpe med at eliminere de beskrevne kodelugte.

Uddrag en klasse

En klasse udfører for mange funktioner. Nogle af dem skal flyttes til en anden klasse. Antag for eksempel, at vi har en Humanklasse, der også gemmer en hjemmeadresse og har en metode, der returnerer den fulde adresse:

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 at placere adresseoplysningerne og den tilhørende metode (databehandlingsadfærd) i en separat 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();
    }
 }

Uddrag en metode

Hvis en metode har en eller anden funktionalitet, der kan isoleres, bør du placere den i en separat metode. For eksempel en metode, der beregner rødderne af en andengradsligning:

    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 hver af de tre mulige muligheder 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 er blevet meget kortere og lettere at forstå.

At passere et helt objekt

Når en metode kaldes med parametre, kan du nogle gange se kode som denne:

 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 dedikeret til at modtage værdier og gemme dem i primitive variable. Nogle gange kan sådanne konstruktioner tage op til 10 linjer. Det er meget lettere at videregive selve objektet og bruge det til at udtrække de nødvendige 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;
 }

Enkel, kort og præcis.

Logisk gruppering af felter og flytte dem ind i en adskilt classDespitekendsgerning, at eksemplerne ovenfor er meget enkle, og når du ser på dem, kan mange af jer spørge, "Hvem gør det?", mange udviklere laver sådanne strukturelle fejl på grund af skødesløshed, manglende vilje til at omstrukturere koden, eller blot en holdning om "det er godt nok".

Hvorfor refaktorering er effektiv

Som et resultat af god refactoring har et program letlæselig kode, udsigten til at ændre dens logik er ikke skræmmende, og at introducere nye funktioner bliver ikke et kodeanalysehelvede, men er i stedet en behagelig oplevelse i et par dage . Du bør ikke overveje, om det ville være nemmere at skrive et program fra bunden. Antag for eksempel, at dit team vurderer, at det arbejde, der kræves for at forstå, analysere og refactor kode, vil være større end at implementere den samme funktionalitet fra bunden. Eller hvis koden, der skal refaktoriseres, har mange problemer, som er svære at fejlfinde. At vide, hvordan man kan forbedre strukturen af ​​kode er afgørende i arbejdet med en programmør. Og at lære at programmere i Java gøres bedst på CodeGym, onlinekurset, der lægger vægt på praksis. 1200+ opgaver med øjeblikkelig verifikation, omkring 20 miniprojekter, spilopgaver — alt dette vil hjælpe dig med at føle dig sikker i kodning. Det bedste tidspunkt at starte er nu :)

Ressourcer til yderligere at fordybe dig i refactoring

Den mest berømte bog om refactoring er "Refactoring. Improving the Design of Existing Code" af Martin Fowler. Der er også en interessant publikation om refactoring, baseret på en tidligere bog: "Refactoring Using Patterns" af Joshua Kerievsky. Apropos mønstre... Når du omfaktorerer, er det altid meget nyttigt at kende grundlæggende designmønstre. Disse fremragende bøger vil hjælpe med dette: Apropos mønstre... Når du omfaktorerer, er det altid meget nyttigt at kende grundlæggende designmønstre. Disse fremragende bøger vil hjælpe med dette:
  1. "Design Patterns" af Eric Freeman, Elizabeth Robson, Kathy Sierra og Bert Bates, fra Head First-serien
  2. "The Art of Readable Code" af Dustin Boswell og Trevor Foucher
  3. "Code Complete" af Steve McConnell, som udstikker principperne for smuk og elegant kode.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION