Codice legacy, usciamo dal pantano! @iad11

55
Codice Legacy, usciamo dal pantano! Simone Casciaroli (@simonecasc) Stefano Leli (@sleli)

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 of Codice legacy, usciamo dal pantano! @iad11

Page 1: Codice legacy, usciamo dal pantano! @iad11

Codice Legacy,usciamo dal pantano!

Simone Casciaroli (@simonecasc)Stefano Leli (@sleli)

Page 2: Codice legacy, usciamo dal pantano! @iad11

Bird Watching Gamehttp://github.com/sleli/BirdWatching

Page 3: Codice legacy, usciamo dal pantano! @iad11

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
Page 5: Codice legacy, usciamo dal pantano! @iad11

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

Rimozione della duplicazioneRimozione degli ifOggetti invece che primitive typeTell don’t ask

Refactoring Hints

Page 7: Codice legacy, usciamo dal pantano! @iad11

Passi di refactoring

Page 8: Codice legacy, usciamo dal pantano! @iad11

Duplicazione

Page 9: Codice legacy, usciamo dal pantano! @iad11

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

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

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

Eliminiamola!

Page 13: Codice legacy, usciamo dal pantano! @iad11

Eliminiamola!@Before public void Setup() { field = new GameField(new FieldSize(10,5,3)); }

Page 14: Codice legacy, usciamo dal pantano! @iad11

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

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

Diamo a Cesareciò che è di Cesare

Page 17: Codice legacy, usciamo dal pantano! @iad11

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
Page 19: Codice legacy, usciamo dal pantano! @iad11

bird.setLocation(location);if (!(bird instanceof Chicken)) bird.setHeight(new Random().nextInt(this.depth));

Page 20: Codice legacy, usciamo dal pantano! @iad11

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

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
Page 23: Codice legacy, usciamo dal pantano! @iad11

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

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

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

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

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

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

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

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

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

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

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

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

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

Gli IF proprio non ci piacciono!

Page 37: Codice legacy, usciamo dal pantano! @iad11

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

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

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

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

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

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

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

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

Di nuovo sulle responsabilità

Page 46: Codice legacy, usciamo dal pantano! @iad11

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

BirdsArmy

Page 48: Codice legacy, usciamo dal pantano! @iad11

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

Doppia personalita’

Page 50: Codice legacy, usciamo dal pantano! @iad11
Page 51: Codice legacy, usciamo dal pantano! @iad11
Page 52: Codice legacy, usciamo dal pantano! @iad11

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

Conclusioni

Rimozione della duplicazioneRimozione degli ifOggetti invece che primitive typeTell don’t ask

Page 54: Codice legacy, usciamo dal pantano! @iad11

Repository

http://github.com/sleli/BirdWatching

Page 55: Codice legacy, usciamo dal pantano! @iad11

GRAZIE

@simonecasc@sleli

http://joind.in/4513