972

Refaktorizace aneb jak umí Visual Studio kouzlit se zdrojovým kódem

autor Lukáš Kouřil | publikováno 13. května 2008


Refaktorizační techniky, které nabízí Visual Studio, jsou velmi mocným nástrojem, jež ušetří spoustu práce při úpravách větších částí zdrojových kódů. Představte si jen takovou drobnost, jakou je přejmenování jedné hojně využívané metody v rámci celého projektu tak, aby zůstala zachována funkčnost všech volání této metody. Díky refaktorizaci je to otázka doslova dvou kliknutí. Refaktorizace toho však nabízí mnohem víc a v tomto článku sami uvidíte, že to, co umí Visual Studio pomocí refaktorizace provádět se zdrojovým kódem, je doslova jako kouzlení.

Visual Studio nabízí celkem 7 refaktorizačních technik. Jsou to: 

  • Přejmenování

  • Extrakce metody

  • Zapouzdření pole

  • Extrakce rozhraní

  • Povýšení lokální proměnné na parametr

  • Odstranění parametrů

  • Přeuspořádání parametrů

 a je možné je vyvolat buď z nabídky Refactor, nebo přes kontextové menu a volbu Refactor.

Co budeme refaktorizovat

Pojďme si nyní ukázat, jak s těmito technikami pracovat a jak pomocí nich "zušlechtit" naprosto hrozný zdrojový kód.

Ve Visual Studiu založíme nový WinForm projekt v jazyce C# a pojmenujeme jej Refaktorizace.

Necháme si zobrazit kód Form1.cs a umístíme do něj následující: 

 1: using System;
 2: using System.Collections.Generic; 
 3: using System.ComponentModel; 
 4: using System.Data; 
 5: using System.Drawing; 
 6: using System.Text; 
 7: using System.Windows.Forms; 
 8: 
 9: namespace Refaktorizace 
10: { 
11:     // nevyhovující název třídy Form1 
12:     public partial class Form1 : Form 
13:     { 
14:         // veřejná vnitřní proměnná 
15:         public int _cislo = 1; 
16: 
17:         public Form1() 
18:         { 
19:             InitializeComponent();
20: 
21:             Metoda(1, 2); 
22: 
23:             int _a = 2; 
24:             int _b = 1; 
25: 
26:             // opakující se kód 
27:             int _vysledek; 
28:             if (_b != 0) 
29:                 _vysledek = _a / _b; 
30:         } 
31: 
32:         // nevhodný název metody 
33:         public int Metoda(int a, int b) 
34:         { 
35:             return a + b; 
36:         } 
37: 
38:         // nevyhovující struktura metody 
39:         public int Mocnina(int a) 
40:         { 
41:             int _exponent = 2; 
42:             return a ^ _exponent; 
43:         } 
44: 
45:         // nevyužité a rozházené parametry 
46:         public int Vypocet(int a, int c, int e, int b, int d) 
47:         { 
48:             int _vysledek = 0; 
49:             if (b != 0) 
50:                 _vysledek = a / b; 
51: 
52:             _vysledek += Metoda(a, b) * Mocnina(c); 
53: 
54:            return _vysledek; 
55:         } 
56:     } 
57: }

  

Jak vidíte, jde o opravdu strašný kód, u nehož jsem do komentáře uvedl "závady", které se zde vyskytují. Pusťme se tedy rychle do refaktorizace.

1. Přejmenování (Rename...)

Jako první, co nám v uvedeném kódu nevyhovuje, je název samotné třídy. Vygenerovaný název Form1 není zrovna příliš vhodný. Název třídy by měl totiž vždy vystihovat záměr existence samotné třídy. Tento název však není obsažen pouze v jednom souboru a navíc se vyskytuje na spoustě míst. Přejmenování pomocí Find->Replace by tedy nebylo příliš vhodné. Nikde nemáme jistotu, že po přejmenování projde kód kompilací.

Protože třída Form1 prezentuje vizuální formulář, můžeme k přejmenování v tomto případě použít vlastnost Name. Nicméně u jiných vlastních tříd tento způsob nebude možný. Použijme tedy refaktorizaci.

Umístěme kurzor na název třídy Form1 na řádku 12 a z kontextového menu vyberme Refactor->Rename...

V dialogovém okně nyní zadáme nový název třídy, např. Main. Navíc zde můžeme pomocí CheckBoxů ovlivnit způsob přejmenování:

  • Preview reference changes - zobrazení náhledu co vše bude přejmenováno
  • Search in comments - prohledávání komentářů
  • Seach in strings - prohledávání řetězců

Pokud zaškrtnete Preview reference changes, můžete v dalším dialogu zvolit, kde všude provádět přejmenování.

Stejným zůsobem přejmenujeme i metodu Metoda, která provádí součet dvou parametrů. Při přejmenovávání nemusíme Rename volat z deklarace příslušného kódu, ale klidně i z jakéhokoliv výskytu daného názvu.  Proveďme tedy refaktorizaci názvu metody Metoda, která je volána na řádku 21 tím, že na ni klikneme a zvolíme z kontextového menu Refactor->Rename... a přejmenujeme ji na Soucet.

Doslova "třešničkou na dortu" této refaktorizační techniky je možnost přejmenovávání celých namespace, tedy prostorů názvů, v nichž se naše aplikace nachází.

2. Extrakce metody (Extract Method...)

Další "závada" na uvedeném zdrojovém kódu je redundantní výskyt kódu. Přesněji jde o kód počítající podíl dvou čísel. První výskyt začíná na řádku 27, druhý na řádku 48. Mnohem lepší by bylo mít metodu pro počítání podílu a následně tuto metodu pouze volat.

Refaktorizační technika Extract Method umožňuje velmi jednoduše vytvořit takovouto metodu z vybraného zdrojového kódu. Extrahujme si tedy metodu pro počítání podílu dvou čísel.

Metodu extrahujeme tím, že se stisknutým levým tlačítkem vybereme kód, který zajišťuje potřebnou funkcionalitu (v našem případě vybereme řádky 28 a 29) a z kontextového menu vybereme Refactor->Extract Method... Následně zadáme název metody (např. Podil) a potvrdíme.

Tím došlo k vytvoření nové metody, v jejímž těle se nachází původní vybraný kód. Visual Studio navíc zajistilo, že vytvořená metoda přebírá dva parametry potřebné pro výpočet a je příslušného návratového typu.

Pozn.: Pro správnou činnost je v tomto případě nutné ve vygenerované metodě defaultně incializovat proměnnou _výsledek.

 3. Zapouzdření pole (Encapsulate Field...)

Když se podíváte hned na začátek vypsaného zdrojového kódu, uvidíte stejnou "hrubku", jako je záměna tvrdého a měkkého "i" v pravopise. Jde o řádek 15, na kterém je uvedena deklarace a inicializace vnitřní proměnné s veřejným modifikátorem přístupu.

Encapsulate Field nám umožní velmi rychlé vypořádání se s tímto problémem pomocí zapouzdření proměnné do vlastnosti.

Proměnnou zapouzdříme tím, že klikneme na její název (v našem případě na název _cislo) a z kontextové nabídky zvolímte Refactor->Encapsulate Field...

Zadáme název vlastnosti do níž bude proměnná zapouzdřena a dále můžeme zvolit několik možností jako tomu bylo i u Rename. Zajímavostí je, že Visual Studio automaticky nabídne název vlastnosti podle normy pro pojmenovávání (tedy odstranění podtržítka "_" a změna prvního písmene z malého na velké).

Potvrzením dojde ke změně modifikátoru přístupu původní proměnné na private a vygenerování veřejné vlastnosti.

4. Extrakce rozhraní (Extract Interface...)

Extrakce rozhraní umožní na základě obsahu třídy vytvořit rozhraní, které je posléze možné použít pro odvozování.

K extrakci stačí umístit kurzor kdekoliv dovnitř třídy a z kontextového menu zvolit Refactor->Extract Interface... Následně v dialogovém okně zadáme název rozhraní a vybereme veřejné členy, které budou rozhraní tvořit. Potvrzením dojde k vygenerování rozhraní a zároveň i odvození aktuální třídy od nového rozhraní.

5. Povýšení lokální proměnné na parametr (Promote Local Variable to Parameter...)

Podívejme se ne metodu Mocnina, která v aktuální podobě počítá druhou mocninu čísla vstupujícího jako parametr do metody. Současný kód je hodně neefektivní, neboť obsahuje deklaraci a inicializaci zbytečné proměnné. Navíc i podoba metody není zrovna nejlepší. Co takto vnitřní proměnnou _exponent povýšit na parametr a tím docílit, aby metoda počítala i mocniny jiných řádů než je 2?

Umístěme kurzor na název proměnné na řádku 41 a z kontextové nabídky zvolme Refactor->Promote Local Variable to Parameter...

Nyní si můžeme všimnout, že do deklarace metody Mocnina skutečně přibyl nový paramter. Navíc bylo upraveno i volání této metody na řádku 52 přidáním hodnoty parametru podle toho, jak byla původní proměnná inicializována.

6. Odstranění (Remove) a přeuspořádání (Regroup) parametrů

Jako poslední nám zbývá upravit metodu Vypocet. Jak vidíte, metoda má spoustu parametrů, které jsou zpřeházené a některé z nich dokonce nevyužité.

Pro jejich odstranění a přeuspořádání slouží refaktorizační techniky Remove Parameters a Regroup Parameters, které lze zavolat z kontextové nabídky vyvolané uvnitř metody. Můžeme tedy odstranit nevyužité parametry d, e a zbývající parametry uspořádat podle abecedy. Jako změny provedené všemi refaktorizačními technikami tak i tyto se provedou nejen u deklarace metody, ale upraví i všechna její volání.

 Závěr

Na velmi jednoduchém a krátkém kousku zdrojového kódu jsme si ukázali, co všechno dokáže refaktorizace, kterou provádí Visual Studio. Tyto techniky však nabývají mnohem větší význam při úpravách rozsáhlých projektů, kdy je jistě oceníte, a to nejen když se vám dostane do ruky kód, který je velmi "nepěkně" napsán. 

Doufám, že jsem vám tímto článkem dokázal, že Visual Studio není jen o psaní kódu, ale také poskytuje velmi vyspělou funkcionalitu, mezi níž manipulace se zdrojovým kódem pomocí refaktorizace bezesporu patří.