Codice legacy, usciamo dal pantano!

1,001 views

Published on

Published in: Sports
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,001
On SlideShare
0
From Embeds
0
Number of Embeds
25
Actions
Shares
0
Downloads
9
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Codice legacy, usciamo dal pantano!

  1. 1. Codice Legacy, usciamo dal pantano! Antonio Carpentieri (@acarpe) Stefano Leli (@sleli)giovedì 30 giugno 2011
  2. 2. Regole • Tempo a disposizione: 50 minuti • Il codice va rifattorizzato per essere conforme ai principi S.O.L.I.D. • I test possono essere modificatigiovedì 30 giugno 2011
  3. 3. S.O.L.I.D. Principlesgiovedì 30 giugno 2011
  4. 4. giovedì 30 giugno 2011
  5. 5. Single Responsibility Principle “There should never be more than one reason for a class to change”giovedì 30 giugno 2011
  6. 6. giovedì 30 giugno 2011
  7. 7. Interface Segregation Principle “Clients should not be forced to depend upon interfaces that they do not use” public interface MobileDevice { void Call(string phoneNumber); void PlayAudio(Uri uri); void PlayVideo(Uri uri); void TakePicture(); void StartRecordingAudio(); void StopRecordingAudio(); void StartRecordingVideo(); void StopRecordingVideo(); }giovedì 30 giugno 2011
  8. 8. giovedì 30 giugno 2011
  9. 9. giovedì 30 giugno 2011
  10. 10. giovedì 30 giugno 2011
  11. 11. Liskov Substitution Principle “Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.”giovedì 30 giugno 2011
  12. 12. Bird Watching Game http://github.com/sleli/BirdWatchinggiovedì 30 giugno 2011
  13. 13. Il Gioco • Simulatore di Bird Watching • Una sorta di “battaglia navale”... ma qui gli assi sono 3 (gli uccelli volano)giovedì 30 giugno 2011
  14. 14. Le Classigiovedì 30 giugno 2011
  15. 15. Analisi del problemagiovedì 30 giugno 2011
  16. 16. Classe GameField Responsabilità Collaborazioni Gestisce la collezione di Birds Dispone il campo da gioco Valida il campo da gioco Classe Bird Inizializza il campo (start) Gestisce le logiche degli shot Il refactoring verterà sulla suddivizione di responsabilità (aumentare la coesione, diminuire l’accoppiamento) •Maggiore riuso del codice •Maggiore robusteza del programma •Minor costo del cambiamentogiovedì 30 giugno 2011
  17. 17. Passi di refactoringgiovedì 30 giugno 2011
  18. 18. Duplicazionegiovedì 30 giugno 2011
  19. 19. Duplicazione (semplice) @Before public void Setup() { field = new GameField(10,5,3, new FieldSize(10,5,3)); }giovedì 30 giugno 2011
  20. 20. Duplicazione (semplice) public GameField(int width, int height, int depth, FieldSize fieldSize) { this.width = width; this.height = height; this.depth = depth; this.fieldSize = fieldSize; birds = new ArrayList<Bird>(); }giovedì 30 giugno 2011
  21. 21. Duplicazione (semplice) //Place the birds on the fields private void placeBirds(PlacingMode type) throws Exception { ... Location location = new Location(new Random().nextInt(this.width), new Random().nextInt(this.height)); bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth)); ... }giovedì 30 giugno 2011
  22. 22. Eliminiamola!giovedì 30 giugno 2011
  23. 23. Eliminiamola! @Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); }giovedì 30 giugno 2011
  24. 24. Eliminiamola! @Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); } public GameField(FieldSize fieldSize) { this.fieldSize = fieldSize; birds = new ArrayList<Bird>(); }giovedì 30 giugno 2011
  25. 25. Eliminiamola! @Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); } public GameField(FieldSize fieldSize) { this.fieldSize = fieldSize; birds = new ArrayList<Bird>(); } //Place the birds on the fields private void placeBirds(PlacingMode type) throws Exception { ... Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.height())); bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(fieldSize.depth())); ... }giovedì 30 giugno 2011
  26. 26. Diamo a Cesare ciò che è di Cesaregiovedì 30 giugno 2011
  27. 27. Diamo a Cesare ciò che è di Cesare public class Location { int x = 0; int y = 0; public Location (int x, int y) { this.x = x; this.y = y; } }giovedì 30 giugno 2011
  28. 28. Diamo a Cesare ciò che è di Cesaregiovedì 30 giugno 2011
  29. 29. Diamo a Cesare ciò che è di Cesare bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth));giovedì 30 giugno 2011
  30. 30. Diamo a Cesare ciò che è di Cesare bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth)); Bird duck = new Duck(); duck.setLocation(new Location(10,5)); duck.setHeight(3);giovedì 30 giugno 2011
  31. 31. Diamo a Cesare ciò che è di Cesare bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth)); Bird duck = new Duck(); duck.setLocation(new Location(10,5)); duck.setHeight(3); int h = bird.getHeight(); Location location = bird.getLocation(); int x = location.x; int y = location.y; isValid = fieldSize.isWithinField(h, x, y);giovedì 30 giugno 2011
  32. 32. Diamo a Cesare ciò che è di Cesare public class Location { int x = 0; int y = 0; int h = 0; public Location (int x, int y) { this(x, y, 0); } public Location (int x, int y, int z) { this.x = x; this.y = y; this.h = z; } }giovedì 30 giugno 2011
  33. 33. Diamo a Cesare ciò che è di Cesare Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.height()), new Random().nextInt(fieldSize.depth()));giovedì 30 giugno 2011
  34. 34. Diamo a Cesare ciò che è di Cesare public abstract class Bird { Location location; int height; public void setHeight(int height) throws Exception{ this.location.h = height; } public int getHeight() { return location.h; } public void setLocation(Location location) { this.location = location; } public Location getLocation() { return location; } public abstract void sing(); }giovedì 30 giugno 2011
  35. 35. Diamo a Cesare ciò che è di Cesare private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { int h = bird.getHeight(); Location location = bird.getLocation(); int x = location.x; int y = location.y; isValid = fieldSize.isWithinField(h, x, y); if (!isValid) break; } return isValid; }giovedì 30 giugno 2011
  36. 36. Diamo a Cesare ciò che è di Cesare private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { Location location = bird.getLocation(); int h = location.h; int x = location.x; int y = location.y; isValid = fieldSize.isWithinField(h, x, y); if (!isValid) break; } return isValid; }giovedì 30 giugno 2011
  37. 37. Diamo a Cesare ciò che è di Cesare public boolean shot(int x, int y, int h) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { int height = bird.getHeight(); Location location = bird.getLocation(); hit = location.x == x && location.y == y && height == h; if (hit) { bird.sing(); break; } } } return hit; }giovedì 30 giugno 2011
  38. 38. Diamo a Cesare ciò che è di Cesare public boolean shot(int x, int y, int h) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { Location location = bird.getLocation(); hit = location.x == x && location.y == y && location.h == h; if (hit) { bird.sing(); break; } } } return hit; }giovedì 30 giugno 2011
  39. 39. Quindi ora? public abstract class Bird { Location location; int height; public void setHeight(int height) throws Exception{ this.height = height; } public int getHeight() { return height; } public void setLocation(Location location) { this.location = location; } public Location getLocation() { return location; } public abstract void sing(); }giovedì 30 giugno 2011
  40. 40. Tell, don’t Ask public boolean shot(int x, int y, int h) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { Location location = bird.getLocation(); hit = location.x == x && location.y == y && location.h == h; if (hit) { bird.sing(); break; } } } return hit; }giovedì 30 giugno 2011
  41. 41. Applied public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = shotLocation.equals(bird.getLocation()); if (hit) { bird.sing(); break; } } } return hit; }giovedì 30 giugno 2011
  42. 42. Let’s apply it another time public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = bird.wasHit(shotLocation) if (hit) { bird.sing(); break; } } } return hit; }giovedì 30 giugno 2011
  43. 43. Another time too public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = bird.wasHit(shotLocation) if (hit) { break; } } } return hit; }giovedì 30 giugno 2011
  44. 44. Programmiamo ad oggetti o a tipi primitivi? private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { Location location = bird.getLocation(); int h = location.h; int x = location.x; int y = location.y; isValid = fieldSize.isWithinField(h, x, y); if (!isValid) break; } return isValid; }giovedì 30 giugno 2011
  45. 45. e allora si fa! private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { isValid = fieldSize.isWithinField(bird.getLocation()); if (!isValid) break; } return isValid; }giovedì 30 giugno 2011
  46. 46. Serve ancora? private void placeBirds(PlacingMode type) throws Exception { //Random Distribution if (type == PlacingMode.Random) { for(Bird bird : birds) { Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.eighth()), new Random().nextInt(fieldSize.depth())); bird.setLocation(location); if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth)); } } //Custom Distribution else if (type == PlacingMode.Custom) { } }giovedì 30 giugno 2011
  47. 47. E allora rimuoviamolo private void placeBirds(PlacingMode type) throws Exception { //Random Distribution if (type == PlacingMode.Random) { for(Bird bird : birds) { Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.eighth()), new Random().nextInt(fieldSize.depth())); bird.setLocation(location); } } //Custom Distribution else if (type == PlacingMode.Custom) { } }giovedì 30 giugno 2011
  48. 48. Anche il metodo public abstract class Bird { ... public void setHeight(int height) throws Exception{ this.height = height; } ... }giovedì 30 giugno 2011
  49. 49. Gli IF proprio non ci piacciono!giovedì 30 giugno 2011
  50. 50. Come procediamo? private void placeBirds(PlacingMode type) throws Exception { //Random Distribution if (type == PlacingMode.Random) { for(Bird bird : birds) { Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.eighth()), new Random().nextInt(fieldSize.depth())); bird.setLocation(location); } } //Custom Distribution else if (type == PlacingMode.Custom) { } }giovedì 30 giugno 2011
  51. 51. I Passi di refactoring • Estratta responsabilità di “Random placing strategy” in una classe • estratta interfaccia IPlacingStrategy • creata class NullPlacingStrategy • creata Factory per Placing strategy • “inlainato” metodo placeBirds • trasformata la factory in un field ed estratto come parametro del costruttoregiovedì 30 giugno 2011
  52. 52. Il risultato public class PlacingStrategyFactory { public interface IPlacingStrategy { public IPlacingStrategy create(PlacingMode type, void place(List<Bird> birds); FieldSize fieldSize) { if (type == PlacingMode.Random) { } return new RandomPlacingStrategy(fieldSize); } public class NullPlacingStrategy implements IPlacingStrategy { return new NullPlacingStrategy(); } @Override public void place(List<Bird> birds) { } // Do nothing } } public class RandomPlacingStrategy implements IPlacingStrategy { private FieldSize fieldSize; public RandomPlacingStrategy(FieldSize fieldSize) { this.fieldSize = fieldSize; } @Override public void place(List<Bird> birds) { for(Bird bird : birds) { Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.height()), new Random().nextInt(fieldSize.depth())); bird.setLocation(location); } } }giovedì 30 giugno 2011
  53. 53. Il risultato public class GameField { ... public boolean startGame(PlacingMode pm) { placingStrategyFactory.create(pm, fieldSize).place(birds); gameStarted = isGameStarted(); return gameStarted; } ... }giovedì 30 giugno 2011
  54. 54. Altri IF che non ci piacciono private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { isValid = fieldSize.isWithinField(bird.getLocation()); if (!isValid) break; } return isValid; }giovedì 30 giugno 2011
  55. 55. Così è meglio private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { isValid = isValid && fieldSize.isWithinField(bird.getLocation()); } return isValid; }giovedì 30 giugno 2011
  56. 56. Stesso discorso public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = bird.wasHit(shotLocation) if (hit) { break; } } } return hit; }giovedì 30 giugno 2011
  57. 57. Diventa public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = hit || bird.wasHit(shotLocation) } } return hit; }giovedì 30 giugno 2011
  58. 58. Di nuovo sulle responsabilitàgiovedì 30 giugno 2011
  59. 59. Notate qualcosa? private boolean isGameFieldValid() { boolean isValid = true; for(Bird bird : birds) { isValid = isValid && fieldSize.isWithinField(bird.getLocation()); } return isValid; } public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = hit || bird.wasHit(shotLocation) } } return hit; }giovedì 30 giugno 2011
  60. 60. BirdList public class BirdList extends ArrayList<Bird> { private static final long serialVersionUID = -3323859086260693300L; public boolean areAllBirdsPlacedWithinField(FieldSize fieldSize) { boolean isValid = true; for(Bird bird : this) { isValid = isValid && fieldSize.isWithinField(bird.getLocation()); } return size() > 0 && isValid; } public boolean anyBirdWasHit(Location shotLocation) { boolean hit=false; for(Bird bird : this) { hit = hit || bird.wasHit(shotLocation); } return hit; } }giovedì 30 giugno 2011
  61. 61. ed ecco i chiamanti private boolean isGameStarted() { return birds.areAllBirdsPlacedWithinField(fieldSize); } public boolean shot(Location shotLocation) { return birds.anyBirdWasHit(shotLocation) && gameStarted; }giovedì 30 giugno 2011
  62. 62. I Repository http://github.com/sleli/BirdWatchinggiovedì 30 giugno 2011
  63. 63. GRAZIE Antonio Carpentieri (@acarpe) Stefano Leli (@sleli)giovedì 30 giugno 2011

×