codice legacy, usciamo dal pantano! @iad11
DESCRIPTION
Avete mai provato la sensazione di essere immersi in una melma di codice putrido e maleodorante e di non riuscire a venirne fuori nonostante tutti i vostri sforzi?In questo workshop si cercherà di simulare proprio questo scenario proponendovi un esempio di progetto mal scritto e che sarete chiamati in prima persona a rifattorizzare.Nel nostro ruolo di coach vi faremo capire come riconoscere il codice che ha bisogno di essere migliorato, ed applicando i solid principles verrete guidati verso un buon design in grado di portarvi in acque più sicure e pulite.TRANSCRIPT
![Page 1: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/1.jpg)
Codice Legacy,usciamo dal pantano!
Simone Casciaroli (@simonecasc)Stefano Leli (@sleli)
![Page 2: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/2.jpg)
Bird Watching Gamehttp://github.com/sleli/BirdWatching
![Page 3: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/3.jpg)
Il Gioco
•Simulatore di Bird Watching
•Una sorta di “battaglia navale”... ma qui gli assi sono 3 (gli uccelli volano)
![Page 4: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/4.jpg)
![Page 5: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/5.jpg)
Classe GameFieldResponsabilità Collaborazioni
Gestisce la collezione di BirdsDispone il campo da giocoValida il campo da giocoInizializza il campo (start)Gestisce le logiche degli shot
Classe Bird
![Page 6: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/6.jpg)
Rimozione della duplicazioneRimozione degli ifOggetti invece che primitive typeTell don’t ask
Refactoring Hints
![Page 7: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/7.jpg)
Passi di refactoring
![Page 8: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/8.jpg)
Duplicazione
![Page 9: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/9.jpg)
Duplicazione (semplice)
@Before public void Setup() { field = new GameField(10,5,3, new FieldSize(10,5,3)); }
![Page 10: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/10.jpg)
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>(); }
![Page 11: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/11.jpg)
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)); ... }
![Page 12: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/12.jpg)
Eliminiamola!
![Page 13: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/13.jpg)
Eliminiamola!@Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); }
![Page 14: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/14.jpg)
Eliminiamola!@Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); }
public GameField(FieldSize fieldSize) { this.fieldSize = fieldSize; birds = new ArrayList<Bird>(); }
![Page 15: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/15.jpg)
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())); ... }
![Page 16: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/16.jpg)
Diamo a Cesareciò che è di Cesare
![Page 17: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/17.jpg)
public class Location { int x = 0; int y = 0; public Location (int x, int y) { this.x = x; this.y = y; }
}
![Page 18: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/18.jpg)
![Page 19: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/19.jpg)
bird.setLocation(location);if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth));
![Page 20: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/20.jpg)
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);
![Page 21: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/21.jpg)
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);
![Page 22: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/22.jpg)
![Page 23: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/23.jpg)
Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.height()), new Random().nextInt(fieldSize.depth()));
![Page 24: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/24.jpg)
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;}
![Page 25: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/25.jpg)
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;}
![Page 26: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/26.jpg)
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; }
![Page 27: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/27.jpg)
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; }
![Page 28: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/28.jpg)
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();}
![Page 29: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/29.jpg)
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) { } }
![Page 30: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/30.jpg)
Tell, don’t Askpublic 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; }
![Page 31: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/31.jpg)
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; }
![Page 32: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/32.jpg)
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; }
![Page 33: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/33.jpg)
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; }
![Page 34: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/34.jpg)
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;}
![Page 35: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/35.jpg)
private boolean isGameFieldValid(){ boolean isValid = true; for(Bird bird : birds) { isValid = fieldSize.isWithinField(bird.getLocation()); if (!isValid) break; } return isValid;}
Text
...ad Oggetti
![Page 36: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/36.jpg)
Gli IF proprio non ci piacciono!
![Page 37: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/37.jpg)
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) { } }
![Page 38: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/38.jpg)
I Passi di refactoring• Estratta responsabilità di “Random placing strategy” in
una classe
• estratta interfaccia IPlacingStrategy
• creata class NullPlacingStrategy
• creata Factory per Placing strategy
• inline metodo placeBirds
• trasformata la factory in un field ed estratto come parametro del costruttore
![Page 39: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/39.jpg)
Il risultatopublic interface IPlacingStrategy {
void place(List<Bird> birds);
}
public class NullPlacingStrategy implements IPlacingStrategy {
@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); } }
}
public class PlacingStrategyFactory {
public IPlacingStrategy create(PlacingMode type, FieldSize fieldSize) { if (type == PlacingMode.Random) { return new RandomPlacingStrategy(fieldSize); } return new NullPlacingStrategy(); }
}
![Page 40: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/40.jpg)
Il risultato
public class GameField {...
public boolean startGame(PlacingMode pm) { placingStrategyFactory.create(pm, fieldSize).place(birds); gameStarted = isGameStarted(); return gameStarted; }
...}
![Page 41: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/41.jpg)
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;}
![Page 42: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/42.jpg)
private boolean isGameFieldValid(){ boolean isValid = true; for(Bird bird : birds) { isValid = isValid && fieldSize.isWithinField(bird.getLocation()); } return isValid;}
what does it mean?
Groovy
Java + Guava
![Page 43: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/43.jpg)
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; }
![Page 44: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/44.jpg)
public boolean shot(Location shotLocation) { boolean hit = false; if (gameStarted) { for(Bird bird : birds) { hit = hit || bird.wasHit(shotLocation) } } return hit; }
or
Groovy
Java + Guava
![Page 45: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/45.jpg)
Di nuovo sulle responsabilità
![Page 46: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/46.jpg)
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; }
![Page 47: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/47.jpg)
BirdsArmy
![Page 48: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/48.jpg)
ed ecco i chiamanti
private boolean isGameStarted() { return birds.areAllBirdsPlacedWithinField(fieldSize);}public boolean shot(Location shotLocation) { return birds.anyBirdWasHit(shotLocation) && gameStarted; }
![Page 49: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/49.jpg)
Doppia personalita’
![Page 50: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/50.jpg)
![Page 51: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/51.jpg)
![Page 52: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/52.jpg)
ConclusioniTest funzionali attorno al codice da refattorizzare
Refactoring ad ogni step e’ l’ideale
Se si parte con codice legacy usate le techniche viste
![Page 53: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/53.jpg)
Conclusioni
Rimozione della duplicazioneRimozione degli ifOggetti invece che primitive typeTell don’t ask
![Page 54: Codice legacy, usciamo dal pantano! @iad11](https://reader033.vdocuments.mx/reader033/viewer/2022052905/558603bdd8b42a81638b46d5/html5/thumbnails/54.jpg)
Repository
http://github.com/sleli/BirdWatching