Een inleiding tot refactoring met IntelliJ IDEA

1. Overzicht

Het overzichtelijk houden van de code is niet altijd eenvoudig. Gelukkig voor ons zijn onze IDE's tegenwoordig behoorlijk slim en kunnen ze ons daarbij helpen. In deze tutorial gaan we ons concentreren op IntelliJ IDEA, de JetBrains Java-code-editor.

We zullen een paar functies zien die door de editor worden aangeboden om code te refactoren, van het hernoemen van variabelen tot het wijzigen van een methodehandtekening.

2. Hernoemen

2.1. Basis hernoemen

Laten we eerst beginnen met de basis: hernoemen. IntelliJ biedt ons de mogelijkheid om verschillende elementen van onze code te hernoemen: typen, variabelen, methoden en zelfs pakketten.

Om een ​​element te hernoemen, moeten we deze stappen volgen:

  • Klik met de rechtermuisknop op het element
  • Activeer het Refactor> Hernoemen keuze
  • Typ de nieuwe elementnaam
  • druk op Enter

Trouwens, we kunnen de eerste twee stappen vervangen door het element te selecteren en op te drukken Shift + F6.

Wanneer geactiveerd, zal de hernoemingsactie zoek in de code voor elk gebruik van het element en verander ze vervolgens met de opgegeven waarde.

Laten we ons een SimpleClass klasse met een slecht genoemde optelmethode, someAdditionMethod, riep in de hoofd methode:

openbare klasse SimpleClass {openbare statische leegte hoofd (String [] args) {nieuwe SimpleClass (). someAdditionMethod (1, 2); } public int someAdditionMethod (int a, int b) {return a + b; }}

Dus als we ervoor kiezen om deze methode te hernoemen in toevoegen, IntelliJ zal de volgende code produceren:

openbare klasse SimpleClass () {openbare statische leegte hoofd (String [] args) {nieuwe SimpleClass (). add (1, 2); } public int add (int a, int b) {return a + b; }}

2.2. Geavanceerd hernoemen

IntelliJ doet echter meer dan zoeken naar codegebruik van onze elementen en deze hernoemen. Er zijn trouwens nog een paar opties beschikbaar. IntelliJ kan ook zoeken naar voorvallen in opmerkingen en tekenreeksen, en zelfs in bestanden die geen broncode bevatten. Wat betreft parameters, het kan ze hernoemen in de klassenhiërarchie in het geval van overschreven methoden.

Die opties zijn beschikbaar door op te drukken Shift + F6 nog een keer voordat we ons element hernoemen en een pop-up zal verschijnen:

De Zoek in opmerkingen en strings optie is beschikbaar voor elke hernoeming. Wat betreft de Zoek naar tekstoccurrences optie, is deze niet beschikbaar voor methodeparameters en lokale variabelen. eindelijk, de Hernoem parameters in hiërarchie optie is alleen beschikbaar voor methodeparameters.

Zo, als er een overeenkomst wordt gevonden met een van deze twee opties, zal IntelliJ deze laten zien en ons de mogelijkheid bieden om u af te melden voor sommige van de wijzigingen (bijvoorbeeld voor het geval het overeenkomt met iets dat geen verband houdt met onze hernoemen).

Laten we wat Javadoc aan onze methode toevoegen en vervolgens de eerste parameter hernoemen, een:

/ ** * Voegt a en b toe * @param a het eerste nummer * @param b het tweede nummer * / public int add (int a, int b) {...}

Door de eerste optie in de bevestigingspop-up aan te vinken, komt IntelliJ overeen met elke vermelding van de parameters in het Javadoc-commentaar van de methode en biedt het aan om ze ook te hernoemen:

/ ** * Voegt firstNumber toe en b * @param firstNumber het eerste nummer * @param b het tweede nummer * / public int add (int firstNumber, int b) {...}

Ten slotte moeten we dat opmerken IntelliJ is slim en zoekt voornamelijk naar gebruik binnen het bereik van het hernoemde element. In ons geval zou dat betekenen dat een opmerking zich buiten de methode bevindt (behalve de Javadoc) en een vermelding bevat voor een zou niet in aanmerking zijn gekomen voor hernoemen.

3. Extractie

Laten we het nu hebben over extractie. Extractie stelt ons in staat om een ​​stukje code te pakken en het in een variabele, een methode of zelfs een klasse te plaatsen. IntelliJ behandelt dit behoorlijk slim omdat het naar vergelijkbare stukjes code zoekt en aanbiedt om ze op dezelfde manier te extraheren.

In dit gedeelte leren we dus hoe u gebruik kunt maken van de extractiefunctie die wordt aangeboden door IntelliJ.

3.1. Variabelen

Laten we eerst beginnen met het extraheren van variabelen. Dat betekent lokale variabelen, parameters, velden en constanten. Om een ​​variabele te extraheren, moeten we deze stappen volgen:

  • Selecteer een uitdrukking die in een variabele past
  • Klik met de rechtermuisknop op het geselecteerde gebied
  • Activeer het Refactor> Extraheren> Variabele / Parameter / Veld / Constante keuze
  • Kies tussen de Vervang alleen dit exemplaar of Vervang alle x instanties opties, indien voorgesteld
  • Voer een naam in voor de geëxtraheerde uitdrukking (als de gekozen uitdrukking niet bij ons past)
  • druk op Enter

Wat betreft het hernoemen, is het mogelijk om sneltoetsen te gebruiken in plaats van het menu te gebruiken. De standaardsnelkoppelingen zijn respectievelijk Ctrl + Alt + V, Ctrl + Alt + P, Ctrl + Alt + F en Ctrl + Alt + C.

IntelliJ zal proberen een naam te raden voor onze geëxtraheerde uitdrukking, op basis van wat de uitdrukking retourneert. Als het niet aan onze behoeften voldoet, kunnen we het wijzigen voordat we de extractie bevestigen.

Laten we illustreren met een voorbeeld. We kunnen ons voorstellen dat we een methode aan ons toevoegen SimpleClass klasse die ons vertelt of de huidige datum tussen twee opgegeven datums ligt:

openbare statische boolean isNowBetween (LocalDate startDate, LocalDate endingDate) {return LocalDate.now (). isAfter (startDate) && LocalDate.now (). isBefore (endDate); }

Laten we zeggen dat we onze implementatie willen veranderen omdat we gebruiken LocalDate.now () twee keer en we willen er zeker van zijn dat het exact dezelfde waarde heeft in beide evaluaties. Laten we gewoon de uitdrukking selecteren en deze extraheren in een lokale variabele, nu:

Dan, onze LocalDate.now () oproep wordt vastgelegd in een lokale variabele:

openbare statische boolean isNowBetween (LocalDate startsDate, LocalDate endingDate) {LocalDate now = LocalDate.now (); return now.isAfter (startDate) && now.isBefore (endDate); }

Door het Vervang alles optie, hebben we ervoor gezorgd dat beide uitdrukkingen tegelijk zijn vervangen.

3.2. Methoden

Laten we nu kijken hoe we methoden kunnen extraheren met IntelliJ:

  • Selecteer de uitdrukking of regels code die passen bij de methode die we willen maken
  • Klik met de rechtermuisknop op het geselecteerde gebied
  • Activeer het Refactor> Extract> Methode keuze
  • Typ de methode-informatie: de naam, de zichtbaarheid en de parameters
  • druk op Enter

Slaan Ctrl + Alt + M na het selecteren van de methode werkt het lichaam ook.

Laten we ons vorige voorbeeld hergebruiken en zeggen dat we een methode willen hebben die controleert of een datum tussen andere datums ligt. Dan zouden we gewoon onze laatste regel in het isNowBetween method en activeer de methode-extractiefunctie.

In het geopende dialoogvenster kunnen we zien dat IntelliJ de drie benodigde parameters al heeft gezien: begin datum, eind datum en nu. Omdat we willen dat deze methode zo algemeen mogelijk is, hernoemen we de nu parameter in datum. En voor cohesiedoeleinden plaatsen we het als de eerste parameter.

Ten slotte geven we onze methode een naam, isDateBetween, en voltooi het extractieproces:

We zouden dan de volgende code krijgen:

openbare statische boolean isNowBetween (LocalDate startsDate, LocalDate endingDate) {LocalDate now = LocalDate.now (); return isDateBetween (nu, startdatum, einddatum); } privé statische boolean isDateBetween (LocalDate date, LocalDate startDate, LocalDate endingDate) {return date.isBefore (endDate) && date.isAfter (startDate); }

Zoals we kunnen zien, veroorzaakte de actie de creatie van het nieuwe isDateBetween methode, ook wel genoemd in de isNowBetween methode. De methode is standaard privé. Dit had natuurlijk kunnen worden gewijzigd met de zichtbaarheidsoptie.

3.3. Klassen

Na dat alles willen we misschien onze datagerelateerde methoden in een specifieke klas krijgen, gericht op datamanagement, laten we zeggen: DateUtils. Nogmaals, dit is vrij eenvoudig:

  • Klik met de rechtermuisknop in de klas met onze elementen die we willen verplaatsen
  • Activeer het Refactor> Extract> Delegeren keuze
  • Typ de klasse-informatie: de naam, het pakket, de te delegeren elementen, de zichtbaarheid van die elementen
  • druk op Enter

Standaard is er geen sneltoets beschikbaar voor deze functie.

Laten we zeggen, voordat we de functie activeren, noemen we onze datumgerelateerde methoden in de hoofd methode:

isNowBetween (LocalDate.MIN, LocalDate.MAX); isDateBetween (LocalDate.of (2019, 1, 1), LocalDate.MIN, LocalDate.MAX);

Vervolgens delegeren we die twee methoden aan een DateUtils klasse met behulp van de optie delegeren:

Het activeren van de functie zou de volgende code produceren:

openbare klasse DateUtils {openbare statische boolean isNowBetween (LocalDate startsDate, LocalDate endingDate) {LocalDate now = LocalDate.now (); return isDateBetween (nu, startdatum, einddatum); } openbare statische boolean isDateBetween (LocalDate date, LocalDate startDate, LocalDate endingDate) {return date.isBefore (endDate) && date.isAfter (startDate); }}

We kunnen zien dat de isDateBetween methode is gemaakt openbaar. Dat is het resultaat van de zichtbaarheidsoptie, die is ingesteld op escaleren standaard. Escaleren betekent dat de zichtbaarheid wordt gewijzigd omzorg ervoor dat de huidige oproepen naar de gedelegeerde elementen nog steeds aan het compileren zijn.

In ons geval, isDateBetween wordt gebruikt in de hoofd methode van SimpleClass:

DateUtils.isNowBetween (LocalDate.MIN, LocalDate.MAX); DateUtils.isDateBetween (LocalDate.of (2019, 1, 1), LocalDate.MIN, LocalDate.MAX);

Daarom is het bij het verplaatsen van de methode nodig om deze niet privé te maken.

Het is echter mogelijk om onze elementen specifiek zichtbaar te maken door de andere opties te selecteren.

4. Inlining

Nu we extractie hebben behandeld, laten we het hebben over de tegenhanger ervan: inlining. Inlining heeft alles te maken met het nemen van een code-element en het vervangen door waar het van gemaakt is. Voor een variabele zou dit de uitdrukking zijn waaraan deze is toegewezen. Voor een methode zou het de body zijn. Eerder zagen we hoe we een nieuwe klasse konden maken en enkele van onze code-elementen eraan konden delegeren. Maar er zijn tijden we willen misschien een methode delegeren aan een bestaande klasse. Dat is waar deze sectie over gaat.

Om een ​​element inline te plaatsen, moeten we met de rechtermuisknop op dit element klikken - ofwel de definitie of een verwijzing ernaar - en de Refactor>In lijn keuze. We kunnen dit ook bereiken door het element te selecteren en op de Ctrl + Alt + N sleutels.

Op dit punt biedt IntelliJ ons meerdere opties, of we nu een variabele of een methode willen inline, of we nu een definitie of een referentie hebben geselecteerd. Deze opties zijn:

  • Inline alle verwijzingen en verwijder het element
  • Inline alle verwijzingen, maar bewaar het element
  • Inline alleen de geselecteerde referentie en het element behouden

Laten we onze isNowBetween methode en verwijder de nu variabele, die nu een beetje overdreven lijkt:

Door deze variabele in te lijnen, zouden we het volgende resultaat krijgen:

openbare statische boolean isNowBetween (LocalDate startDate, LocalDate endingDate) {return isDateBetween (LocalDate.now (), startDate, endingDate); }

In ons geval was de enige optie om alle verwijzingen te verwijderen en het element te verwijderen. Maar laten we ons voorstellen dat we ook de isDateBetween bel en kies ervoor om het inline te plaatsen. Vervolgens zou IntelliJ ons de drie mogelijkheden bieden waar we eerder over spraken:

Als u de eerste kiest, worden alle aanroepen vervangen door de hoofdtekst van de methode en wordt de methode verwijderd. Wat betreft de tweede, het zou alle aanroepen vervangen door de body van de methode, maar de methode behouden. En tot slot zou de laatste alleen de huidige aanroep vervangen door de hoofdtekst van de methode:

openbare klasse DateUtils {openbare statische boolean isNowBetween (LocalDate startsDate, LocalDate endingDate) {LocalDate date = LocalDate.now (); return date.isBefore (endDate) && date.isAfter (startDate); } openbare statische boolean isDateBetween (LocalDate date, LocalDate begindatum, LocalDate endingDate) {return date.isBefore (endDate) && date.isAfter (begindatum); }}

Onze hoofd methode in de SimpleClass blijft ook onaangetast.

5. Verhuizen

Eerder hebben we gezien hoe we een nieuwe klasse kunnen maken en enkele van onze code-elementen eraan kunnen delegeren. Maar er zijn tijden we willen misschien een methode delegeren aan een bestaande klasse. Dat is waar deze sectie over gaat.

Om een ​​element te verplaatsen, moeten we deze stappen volgen:

  • Selecteer het element dat u wilt verplaatsen
  • Klik met de rechtermuisknop op het element
  • Activeer het Refactor> Verplaatsen keuze
  • Kies de ontvangerklasse en de zichtbaarheid van de methode
  • druk op Enter

Dit kunnen we ook bereiken door op te drukken F6 na het selecteren van het element.

Laten we zeggen dat we een nieuwe methode aan onze SimpleClass, isDateOutstide (), dat ons zal vertellen of een datum zich buiten een interval van datums bevindt:

openbare statische boolean isDateOutside (LocalDate date, LocalDate startDate, LocalDate endingDate) {return! DateUtils.isDateBetween (date, startDate, endingDate); }

We realiseren ons dan dat zijn plaats in ons moet zijn DateUtils klasse. Dus besluiten we het te verplaatsen:

Onze methode is nu in de DateUtils klasse. We kunnen zien dat de verwijzing naar DateUtils binnen de methode is verdwenen omdat deze niet langer nodig is:

openbare statische boolean isDateOutside (LocalDate date, LocalDate startDate, LocalDate endingDate) {return! isDateBetween (date, startDate, endingDate); }

Het voorbeeld dat we zojuist hebben gedaan, werkt goed omdat het een statische methode betreft. In het geval van een instantiemethode zijn de dingen echter niet zo eenvoudig.

Als we een instantiemethode willen verplaatsen, IntelliJ zoekt naar klassen waarnaar wordt verwezen in de velden van de huidige klasse en biedt aan om de methode naar een van die klassen te verplaatsen (op voorwaarde dat ze van ons zijn om te wijzigen).

Als er in de velden naar geen wijzigbare klasse wordt verwezen, stelt IntelliJ voor om de methode te maken statisch voordat u het verplaatst.

6. Een methodehandtekening wijzigen

Ten slotte zullen we het hebben over een functie waarmee we de handtekening van een methode kunnen wijzigen. Het doel van deze functie is om elk aspect van de handtekening van een methode te manipuleren.

Zoals gewoonlijk moeten we een paar stappen doorlopen om de functie te activeren:

  • Selecteer de methode die u wilt wijzigen
  • Klik met de rechtermuisknop op de methode
  • Activeer het Refactor> Handtekening wijzigen keuze
  • Breng wijzigingen aan in de handtekening van de methode
  • druk op Enter

Als we liever de sneltoets voor toetsenborden gebruiken, het is mogelijk om te gebruiken Ctrl + F6 ook.

Deze functie opent een dialoogvenster dat erg lijkt op de functie voor methode-extractie. En dus we hebben dezelfde mogelijkheden die wanneer we een methode extraheren: het wijzigen van de naam, de zichtbaarheid en ook het toevoegen / verwijderen van parameters en het verfijnen ervan.

Laten we ons voorstellen dat we onze implementatie van willen veranderen isDateBetween om de datumbegrenzingen als inclusief of exclusief te beschouwen. Om dat te doen willen we een boolean parameter aan onze methode:

Door de methodehandtekening te wijzigen, kunnen we deze parameter toevoegen, een naam geven en een standaardwaarde geven:

openbare statische boolean isDateBetween (LocalDate date, LocalDate startDate, LocalDate endingDate, boolean inclusive) {return date.isBefore (endDate) && date.isAfter (startDate); }

Daarna hoeven we alleen maar de methodiek aan te passen aan onze behoeften.

Als we wilden, hadden we het kunnen controleren Delegeren via overbelastingsmethode optie om een ​​andere methode met de parameter te maken in plaats van de huidige te wijzigen.

7. Omhoog trekken en omlaag duwen

Onze Java-code heeft doorgaans klassehiërarchieën - afgeleide klassen breiden een basisklasse uit.

Soms willen we leden (methoden, velden en constanten) tussen deze klassen verplaatsen. Dat is waar de laatste refactoring van pas komt: het stelt ons in staat trek leden uit een afgeleide klasse naar de basisklasse of duw ze omlaag vanuit een basisklasse naar elke afgeleide klasse.

7.1. Optrekken

Laten we eerst een methode naar de basisklasse trekken:

  • Selecteer een lid van een afgeleide klasse om op te halen
  • Klik met de rechtermuisknop op het lid
  • Activeer het Refactor> Leden omhoog trekken… keuze
  • Druk op de Refactor knop

Standaard is er geen sneltoets beschikbaar voor deze functie.

Laten we zeggen dat we een afgeleide klasse hebben genaamd Afgeleid. Het maakt gebruik van een privé doubleValue () methode:

public class Derived breidt Base {public static void main (String [] args) {Derived subject = new Derived (); System.out.println ("Verdubbeling 21. Resultaat:" + subject.doubleValue (21)); } private int doubleValue (int nummer) {retour nummer + nummer; }}

De basisklasse Baseren is leeg.

Dus wat gebeurt er als we stoppen? doubleValue () in Baseren?

Er gebeuren twee dingen doubleValue () wanneer we op "Refactor" drukken in het bovenstaande dialoogvenster:

  • het beweegt naar de Baseren klasse
  • zijn zichtbaarheid verandert van privaat naar beschermd zodat de Afgeleid klasse kan het nog steeds gebruiken

De Baseren klasse daarna heeft nu de methode:

openbare klasse Base {protected int doubleValue (int number) {return number + number; }}

Het dialoogvenster voor het ophalen van leden (hierboven afgebeeld) geeft ons wat meer opties:

  • we kunnen andere leden selecteren en ze allemaal tegelijk tevoorschijn halen
  • we kunnen een voorbeeld van onze wijzigingen bekijken met de knop "Voorbeeld"
  • alleen methoden hebben een selectievakje in de kolom "Maak abstract". Indien aangevinkt, geeft deze optie de basisklasse een abstracte methodedefinitie tijdens de pull-up. De feitelijke methode blijft in de afgeleide klasse, maar krijgt een @Override annotatie. Bijgevolg zullen andere afgeleide klassen niet meer compileren dan omdat ze de implementatie van die nieuwe, abstracte basismethode missen

7.2. Neerduwen

Laten we tot slot een lid naar de afgeleide klasse duwen. Dit is het tegenovergestelde van het optrekken dat we zojuist hebben uitgevoerd:

  • Selecteer een lid van een basisklasse om naar beneden te drukken
  • Klik met de rechtermuisknop op het lid
  • Activeer het Refactor> Leden naar beneden duwen ... keuze
  • Druk op de Refactor knop

Net als bij het omhoog trekken van leden, is er standaard geen sneltoets beschikbaar voor deze functie.

Laten we de methode die we zojuist hebben gebruikt opnieuw naar beneden halen. De Baseren klasse zag er als volgt uit aan het einde van de vorige sectie:

openbare klasse Base {protected int doubleValue (int number) {return number + number; }}

Laten we nu pushen doubleValue () tot aan de Afgeleid klasse:

Dit is de Afgeleid class nadat u op "Refactor" hebt gedrukt in het bovenstaande dialoogvenster.De doubleValue () methode is terug:

public class Derived breidt Base {private int theField = 5 uit; public static void main (String [] args) {Afgeleide onderwerp = nieuwe Afgeleide (); System.out.println ("Verdubbeling 21. Resultaat:" + subject.doubleValue (21)); } beschermde int doubleValue (int nummer) {retour nummer + nummer; }}

Nu zowel de Baseren klasse en de Afgeleid klas zijn terug naar waar ze begonnen in de vorige "Pull Up" -sectie. Dat is bijna - doubleValue () hield de beschermd zichtbaarheid het had in Baseren (het was privaat oorspronkelijk).

IntelliJ 2019.3.4 geeft eigenlijk een waarschuwing weer bij het naar beneden duwen doubleValue (): "Gepushte leden zullen niet zichtbaar zijn vanaf bepaalde oproepsites". Maar zoals we kunnen zien in de Afgeleid klasse hierboven, doubleValue () is inderdaad zichtbaar voor de hoofd() methode.

Het dialoogvenster voor het naar beneden duwen van leden (hierboven afgebeeld) geeft ons ook wat meer opties:

  • als we meerdere afgeleide klassen hebben, zal IntelliJ leden naar elke afgeleide klasse pushen
  • we kunnen meerdere leden naar beneden duwen
  • we kunnen een voorbeeld van onze wijzigingen bekijken met de knop "Voorbeeld"
  • alleen methoden hebben een selectievakje in de kolom "Abstract behouden" - Dat is vergelijkbaar met het ophalen van leden: Indien aangevinkt, laat deze optie een abstracte methode achter in de basisklasse. In tegenstelling tot het ophalen van leden, zal deze optie methode-implementaties in alle afgeleide klassen plaatsen. Deze methoden krijgen ook een @Override annotatie

8. Conclusie

In dit artikel hebben we de kans gehad om diep te duiken in enkele van de refactoring-functies die door IntelliJ worden aangeboden. We hebben natuurlijk niet alle mogelijkheden besproken, aangezien IntelliJ een zeer krachtig hulpmiddel is. Voor meer informatie over deze editor kunnen we altijd de documentatie raadplegen.

We hebben een paar dingen gezien, zoals hoe we onze code-elementen een andere naam kunnen geven en hoe we bepaald gedrag kunnen extraheren in variabelen, methoden of klassen. We hebben ook geleerd hoe we sommige elementen inline kunnen plaatsen als we ze niet alleen nodig hebben, code ergens anders kunnen verplaatsen of zelfs een bestaande methodehandtekening volledig kunnen wijzigen.