SlideShare une entreprise Scribd logo
1  sur  23
Télécharger pour lire hors ligne
XPUG Marche – 2° Meeting




                           Workshop sul
                            Refactoring



                                   Stefano Leli
“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.”




                                       Martin Fowler
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
Regole

• Tempo a disposizione 25 minuti

• I test devono restare verdi

• I test non possono essere modificati
Analisi del problema
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 + " kcaln";
          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;
        }
    }
}
Classe CaloriesCalculator
                    Responsabilità                            Collaborazioni

       Validazione delle componenti                 Classe Food
       Costruzione del report di stampa             Classe Person
       Calcolo delle calorie di una lista di cibi   Classe FirstException
       Calcolo dei Km da percorrere                 Classe SecondException

                                 CRC Card 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
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 + " kcaln";
  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;
}
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;
        }
    }
}
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 + " kcaln";
  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;
}
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 + " kcaln";
  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;
}
Information Hiding


 public class Food
 {
    public string Name;
    public double Kg;
    public Food(string name, double kg)
    {
        this.Name = name;
        this.Kg = kg;
    }
}
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 + " kcaln";
  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;
}
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 + " kcaln";
  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;
}
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 + " kcaln";
  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;
}
Passi di Refactoring
Visione di alto livello
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.)
Validazione

     Assegnata alla classe Person la responsabilità
        della propria validazione



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

        Permette di aggiungere nuovi
        comportamenti indipendenti dalla struttura
        dati

        Diminuisce la complessità della struttura
        dati
Calcolo delle Calorie

          Utilizzo del pattern strategy per il calcolo delle
              calorie:


             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.
Costruzione dei Report

           Utilizzo del pattern template per modellare le
               logiche di reporting:


              Permette di separe le parti invarianti da
              quelle 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 è stata
              aggiunta l'interfaccia IDietReport
Riferimenti

Martin Fowler -
Refactoring: Improving the Design of Existing Code




Michael Feathers -
Working Effectively with Legacy Code
Domande?

Contenu connexe

Plus de Stefano Leli

Agile retrospective,an example
Agile retrospective,an exampleAgile retrospective,an example
Agile retrospective,an exampleStefano Leli
 
User stories writing - Codemotion 2013
User stories writing   - Codemotion 2013User stories writing   - Codemotion 2013
User stories writing - Codemotion 2013Stefano Leli
 
User Stories Writing
User Stories WritingUser Stories Writing
User Stories WritingStefano Leli
 
Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Stefano Leli
 
Il project manager e lo sviluppo agile. Separati in casa?
Il project manager e lo sviluppo agile. Separati in casa?Il project manager e lo sviluppo agile. Separati in casa?
Il project manager e lo sviluppo agile. Separati in casa?Stefano Leli
 
Codice legacy, usciamo dal pantano!
Codice legacy, usciamo dal pantano!Codice legacy, usciamo dal pantano!
Codice legacy, usciamo dal pantano!Stefano Leli
 
Manage software dependencies with ioc and aop
Manage software dependencies with ioc and aopManage software dependencies with ioc and aop
Manage software dependencies with ioc and aopStefano Leli
 
Design Pattern In Pratica
Design Pattern In PraticaDesign Pattern In Pratica
Design Pattern In PraticaStefano Leli
 

Plus de Stefano Leli (9)

Agile retrospective,an example
Agile retrospective,an exampleAgile retrospective,an example
Agile retrospective,an example
 
User stories writing - Codemotion 2013
User stories writing   - Codemotion 2013User stories writing   - Codemotion 2013
User stories writing - Codemotion 2013
 
User Stories Writing
User Stories WritingUser Stories Writing
User Stories Writing
 
Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11
 
XP Game
XP GameXP Game
XP Game
 
Il project manager e lo sviluppo agile. Separati in casa?
Il project manager e lo sviluppo agile. Separati in casa?Il project manager e lo sviluppo agile. Separati in casa?
Il project manager e lo sviluppo agile. Separati in casa?
 
Codice legacy, usciamo dal pantano!
Codice legacy, usciamo dal pantano!Codice legacy, usciamo dal pantano!
Codice legacy, usciamo dal pantano!
 
Manage software dependencies with ioc and aop
Manage software dependencies with ioc and aopManage software dependencies with ioc and aop
Manage software dependencies with ioc and aop
 
Design Pattern In Pratica
Design Pattern In PraticaDesign Pattern In Pratica
Design Pattern In Pratica
 

Workshop Su Refactoring

  • 1. XPUG Marche – 2° Meeting Workshop sul Refactoring Stefano Leli
  • 2. “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.” Martin Fowler
  • 3. 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
  • 4. Regole • Tempo a disposizione 25 minuti • I test devono restare verdi • I test non possono essere modificati
  • 6. 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 + " kcaln"; 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; } } }
  • 7. Classe CaloriesCalculator Responsabilità Collaborazioni Validazione delle componenti Classe Food Costruzione del report di stampa Classe Person Calcolo delle calorie di una lista di cibi Classe FirstException Calcolo dei Km da percorrere Classe SecondException CRC Card 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
  • 8. 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 + " kcaln"; 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; }
  • 9. 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; } } }
  • 10. 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 + " kcaln"; 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; }
  • 11. 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 + " kcaln"; 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; }
  • 12. Information Hiding public class Food { public string Name; public double Kg; public Food(string name, double kg) { this.Name = name; this.Kg = kg; } }
  • 13. 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 + " kcaln"; 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; }
  • 14. 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 + " kcaln"; 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; }
  • 15. 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 + " kcaln"; 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; }
  • 17. Visione di alto livello
  • 18. 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.)
  • 19. Validazione Assegnata alla classe Person la responsabilità della propria validazione Adottato l'utilizzo del pattern Visitor per la validazione dei Food: Permette di aggiungere nuovi comportamenti indipendenti dalla struttura dati Diminuisce la complessità della struttura dati
  • 20. Calcolo delle Calorie Utilizzo del pattern strategy per il calcolo delle calorie: 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.
  • 21. Costruzione dei Report Utilizzo del pattern template per modellare le logiche di reporting: Permette di separe le parti invarianti da quelle 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 è stata aggiunta l'interfaccia IDietReport
  • 22. Riferimenti Martin Fowler - Refactoring: Improving the Design of Existing Code Michael Feathers - Working Effectively with Legacy Code