Deel I: Wegwerken van duplicate code met "Extract Method"

Je hebt ongetwijfeld gemerkt dat een aantal code-fragmenten meerdere keren voorkomt. Dergelijke terugkerende code-fragmenten noemen we duplicated code. Duplicated code komt voor doordat een ontwikkelaar herkent heeft dat een eerder geschreven stuk code herbruikbaar is. Het nadeel van het dupliceren van code is dat onderhoud aan dat stukje code op meerdere plaatsen uitgevoerd zal moeten worden. Daarom willen we duplicated code liefst wegwerken.

Oefening I.1: Wegwerken van duplicate code.

Deze oefening is een begeleide oefening. Dit betekent dat elke stap begeleid wordt door gedetailleerde beschrijvingen van de veranderingen die je moet aanbrengen. Deze veranderingen worden telkens geïllustreerd aan de hand van code-fragmenten. Als je deze oefening stap voor stap mee uitvoert, zal je in staat zijn de volgende, soortgelijke oefeningen zelfstandig uit te voeren.

In de klasse lanSimulation::Network vinden we in methode printDocument duplicated code, zoals weergegeven in onderstaande code-fragmenten.

Fragment (a.1)

Fragment (b.1)

Fragmenten (a.1) en (b.1) zijn erg gelijkaardig; op de laatste lijn na zijn ze identiek. Je kan deze gelijkaardigheid benadrukken door de code zó te veranderen dat duidelijk afgebakenend wordt hoe de fragmenten verschillen.

Voer de testen uit om jezelf ervan te verzekeren dat je vóór het refactoren over een correct systeem beschikt.

Door een variabele jobType te introduceren om het verschil tussen fragmenten (a.1) en (b.1) af te bakenen krijg je volgende indeling. Deze refactoring wordt trouwens Introduce Explaining Variable
Postscript job
Fragment (a.2)
ASCII job
Fragment (b.2)

Voer de testen uit om jezelf ervan te verzekeren dat je na het refactoren nog steeds over een correct systeem beschikt.

Het enige waarin fragmenten (a.2) en (b.2) nu nog verschillen, is de waarde van variabele jobType in de eerste regel. Dit betekent dat de laatste 6 regels in fragmenten (a.2) en (b.2) gelijk zijn. Om er nu voor te zorgen dat je eventuele aanpassingen aan deze 6 regels slechts één keer zou moeten uitvoeren, kan je deze 6 regels best in een aparte methode plaatsen, bv. met naam accountForPrintJob:
veralgemening van fragmenten (a.1) en (b.1)
Fragment (c.1)

Fragment (a.3)

Fragment (b.3)

Het afzonderen van lijnen code in een aparte methode noemen we het extraheren van een methode. De refactoring die hiermee overeenkomt heet Extract Method. Aangezien de 6 identieke lijnen gebruik maakten variabelen report, author, title en jobType heeft de geëxtraheerde methode accountForPrintJob in Fragment (c.1) 4 parameters nodig.

Voer de testen uit om jezelf ervan te verzekeren dat je na de Extract Method nog steeds over een correct systeem beschikt.

Merk op dat na het extraheren van de methode accountForPrintJob fragmenten (a.3) en (b.3) erg eenvoudig geworden zijn. Indien je Extract Method in een extreme vorm toepast, krijg je zo 2 soorten methoden:

  1. Coordinatie-methoden: Deze methoden coördineren het werk van andere methoden, en zien er typisch uit als doeX(); doeY(); doeZ();.

  2. Werkmethoden: Deze voeren een elementaire stap in de verwerking van het programma uit.

Hierbij is het uiteraard belangrijk dat de naam van de methode een korte samenvatting vormt van het doel van de methode. Coördinatie-methoden zullen typisch namen hebben die meer lijken op scenario's (bv. requestBroadcast). Werkmethoden, daarentegen, zullen eerder namen hebben die overeenkomen met typische acties binnen zo'n scenario (bv. send, accept)

P.S.: Wanneer een methode geen lokale attributen (attributen van dezelfde klasse) gebruikt, kan je dit best aangeven door de methode als statisch te declareren. Zo maak je duidelijk dat je deze methode niet op een instantie van de klasse moet oproepen (bv. x.accountForPrintJob(...)), maar dat je de methode op de klasse zelf kan oproepen (Network::accountForPrintJob(...)), zoals gedaan in fragmenten (a.3) en (b.3).

Oefening I.2: Tweede oefening op het wegwerken van duplicate code

Deze oefening is een zelfstandige oefening met eindevaluatie. Dit betekent dat je de oefening zelf hoort te kunnen uitvoeren. Evenwel kan je nadien je eigen oplossing vergelijken met een referentie-oplossing.

In methoden Network::requestWorkstationPrintsDocument en Network::requestBroadcast merk je op 3 plaatsen logging-code, gelijkaardig aan fragment (d.1) dat hieronder weergegeven wordt.


Fragment (d.1)

Voer de testen uit om jezelf ervan te verzekeren dat je vóór het refactoren over een correct systeem beschikt.

Tracht de 3 duplicaten van logging-code zelfstandig weg te werken door jezelf volgende vragen te stellen:

Voer na de Extract Method de testen uit om jezelf ervan te verzekeren dat je nog steeds over een correct systeem beschikt.

Eén van de mogelijke resultaten van het extraheren van een methode voor code-fragmenten gelijkaardig aan fragment (d.1) wordt hier weergegeven.

Discussie

In oefeningen I.1 en I.2 heb je enkele keren de Extract Method refactoring uitgevoerd. Het doel van deze discussie is het aansnijden van de toepassing van refactorings in je alledaagse werkomgeving.

Discussieer in groep over volgende discussie-punten:

  1. Vertrouwen:
    Hoe zeker ben je dat deze refactorings het gedrag van de lan-simulatie niet veranderd hebben? Welke neven-effecten kunnen er optreden bij het toepassen van de Extract Method refactoring? Zal je deze neven-effecten altijd opmerken?

  2. Nut:
    Vind je dat de refactorings die je net hebt uitgevoerd (voor het wegwerken van duplicate code) de moeite lonen? Wat zijn de kosten (of nadelen) van deze refactorings, en wat zijn de geassocieerde baten (of voordelen)? Wanneer zou je dergelijke refactorings wel uitvoeren, en wanneer niet? Welke argumenten kan je gebruiken om de benodigde tijd bij je baas/klant los te weken?

  3. Toepasbaarheid:
    Bevat je huidige software-project duplicate code? Denk je dat het nuttig is om deze duplicaten in je huidige project weg te werken?

  4. Tool-support:
    Heeft Eclipse je geholpen bij het uitvoeren van deze refactorings? Op welke manier kan een tool je bij deze refactorings helpen? Beschik je in je werkomgeving over dergelijke tools?

Je kan je afvragen waarom je deze Extract Method refactorings eigenlijk écht wil toepassen. Eén van de meest typerende gebruiksscenario's is het ontwarren van grote, complexe methoden. Dergelijke methoden verweven vaak een heleboel verantwoordelijkheden. Sommige van die verantwoordelijkheden behoren werkelijk tot de lijst van verantwoordelijkheden van de huidige klasse. Vaak echter, behoren een aantal van die verantwoordelijkheden eerder tot andere klassen. Daarom is het nuttig om deze verantwoordelijkheden eerst in aparte methoden te encapsuleren, zodat je deze in een volgende fase naar de juiste klasse kan verplaatsen.

Op het verplaatsen van methode maken we in hetvolgende deel oefeningen.