workshop su refactoring

23
Workshop sul Refactoring Stefano Leli XPUG Marche – 2° Meeting

Upload: stefano-leli

Post on 21-Jun-2015

538 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Workshop Su Refactoring

Workshop sulRefactoring

Stefano Leli

XPUG Marche – 2° Meeting

Page 2: Workshop Su Refactoring

Martin Fowler

“Refactoring is the process of changing a software system in such a way that it does not

alter the external behavior of the code yet improves its internal structure.”

Page 3: Workshop Su Refactoring

Principi del Refactoring

• Migliorare il design del codice

• Eliminare le duplicazioni

• Rendere il codice più leggibile e facile da modificare

Single Responsibility Principle (SRP)

Una classe dovrebbe avere una sola ragione per cambiare

Page 4: Workshop Su Refactoring

Regole

• Tempo a disposizione 25 minuti

• I test devono restare verdi

• I test non possono essere modificati

Page 5: Workshop Su Refactoring

Analisi del problema

Page 6: Workshop Su Refactoring

Classe CaloriesCalculator

namespace Kata{

public class CaloriesCalculator {private readonly List<Food> foods;private readonly Person person;

public CaloriesCalculator(Person person, List<Food> foods) {this.person = person;this.foods = foods;

}

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {

Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 /* calories in one kilometer */ * person.Size / cal;foreach (Food type in foods) {if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}}

}

Page 7: Workshop Su Refactoring

Classe CaloriesCalculator

Il refactoring verterà sulla: � suddivisione di responsabilità (aumentare la coesione) � diminuzione delle collaborazioni con le classi applicative (diminuire l'accoppiamento)

Favorendo in questo modo un:� maggiore riuso del codice� maggiore robustezza del programma

Responsabilità Collaborazioni

Validazione delle componentiCostruzione del report di stampaCalcolo delle calorie di una lista di cibiCalcolo dei Km da percorrere

Classe FoodClasse PersonClasse FirstExceptionClasse SecondException

CRC Card Classe CaloriesCalculator

Page 8: Workshop Su Refactoring

Magic Number

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 9: Workshop Su Refactoring

Uso parziale di costanti

public class Person{

public const int MEDIUM_SIZE = 2;public const int FAT = 3;public const int SLIM = 1;

public Person(string name, int kg) {this._name = name;this.kg = kg;

}

private string _name;public string Name {

get { return _name; }set { _name = value; }

}

private int kg;public int Kg {

get { return kg; }set { kg = value; }

}

public int Size {get{

if (kg > 130) return 3;if (kg < 50) return 1;return MEDIUM_SIZE;

}}

}

Page 10: Workshop Su Refactoring

Cicli

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 11: Workshop Su Refactoring

Nomi di variabili metodi e classi

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 12: Workshop Su Refactoring

Information Hiding

public class Food{

public string Name;public double Kg;public Food(string name, double kg){

this.Name = name;this.Kg = kg;

}}

Page 13: Workshop Su Refactoring

Responsabilità:Validazione

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 14: Workshop Su Refactoring

Responsabilità:Calcolo delle calorie e dei Km

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 15: Workshop Su Refactoring

Responsabilità:Costruzione del report

public string Result() {string str = "Diet Report for " + person.Name +":\n";double cal = 0.0;for (int i = 0; i < foods.Count; i++) {Food food = foods[i];str += food.Name + " - " + food.Kg + "\n";if ("".Equals(food.Name)) throw new FirstException();if ("apple".Equals(food.Name)) cal += food.Kg * 15 * 10;if ("magicPill".Equals(food.Name)) cal -= 10;if ("candy".Equals(food.Name)) cal += food.Kg;

}str += "Total: " + (double)cal + " kcal\n";str += "kilometers to be run: " + 90 * person.Size / cal;foreach (Food type in foods) {

if ("".Equals(type.Name)) throw new FirstException();if (person.Kg > 1000) throw new SecondException();

}return str;

}

Page 16: Workshop Su Refactoring

Passi di Refactoring

Page 17: Workshop Su Refactoring

Visione di alto livello

Page 18: Workshop Su Refactoring

Primi Passi

• Effettuate le operazioni individuate in precedenza(Naming, Information Hiding….)

• Creata la classe Meal• Responsabile della validazione di una lista di Food

• Aumenta la coesione tra le classi

• Creata la classe Dietist• Funge da “Mediator”

• Lascia aperti scenari ad estensioni future (DI, etc.)

Page 19: Workshop Su Refactoring

Validazione

Assegnata alla classe Person la responsabilitàdella propria validazione

Adottato l'utilizzo del pattern Visitor per la validazione dei Food:

� Permette di aggiungere nuovicomportamenti indipendenti dalla strutturadati

� Diminuisce la complessità della strutturadati

Page 20: Workshop Su Refactoring

Calcolo delle Calorie

Utilizzo del pattern strategy per il calcolo dellecalorie:

� Maggior grado di disaccoppiamento tra l'implementazione dell'algoritmo e la sua esecuzione

� Possibilità di incapsulare più strategie o algoritmi in classi separate semplicemente implementando le apposite interfacce.

Page 21: Workshop Su Refactoring

Costruzione dei Report

Utilizzo del pattern template per modellare le logiche di reporting:

� Permette di separe le parti invarianti daquelle varianti di un algoritmo.

� Rende facile l'aggiunta di un nuovo report. Basta implementare il template di base e definire le parti varianti

� Per favorire l'information hiding è stataaggiunta l'interfaccia IDietReport

Page 22: Workshop Su Refactoring

Riferimenti

� Martin Fowler -

Refactoring: Improving the Design of Existing Code

� Michael Feathers -

Working Effectively with Legacy Code

Page 23: Workshop Su Refactoring

Domande?