Codice legacy, usciamo dal pantano! @iad11

Post on 21-Jun-2015

448 views 2 download

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

Codice Legacy,usciamo dal pantano!

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

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

Il Gioco

•Simulatore di Bird Watching

•Una sorta di “battaglia navale”... ma qui gli assi sono 3 (gli uccelli volano)

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

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

Refactoring Hints

Passi di refactoring

Duplicazione

Duplicazione (semplice)

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

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>(); }

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)); ... }

Eliminiamola!

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

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

public GameField(FieldSize fieldSize) { this.fieldSize = fieldSize; birds = new ArrayList<Bird>(); }

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())); ... }

Diamo a Cesareciò che è di Cesare

public class Location { int x = 0; int y = 0; public Location (int x, int y) { this.x = x; this.y = y; }

}

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

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);

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);

Location location = new Location(new Random().nextInt(fieldSize.width()), new Random().nextInt(fieldSize.height()), new Random().nextInt(fieldSize.depth()));

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;}

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;}

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; }

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; }

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();}

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) { } }

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; }

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; }

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; }

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; }

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;}

private boolean isGameFieldValid(){ boolean isValid = true; for(Bird bird : birds) { isValid = fieldSize.isWithinField(bird.getLocation()); if (!isValid) break; } return isValid;}

Text

...ad Oggetti

Gli IF proprio non ci piacciono!

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) { } }

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

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(); }

}

Il risultato

public class GameField {...

public boolean startGame(PlacingMode pm) { placingStrategyFactory.create(pm, fieldSize).place(birds); gameStarted = isGameStarted(); return gameStarted; }

...}

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;}

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

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; }

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

Di nuovo sulle responsabilità

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; }

BirdsArmy

ed ecco i chiamanti

private boolean isGameStarted() { return birds.areAllBirdsPlacedWithinField(fieldSize);}public boolean shot(Location shotLocation) { return birds.anyBirdWasHit(shotLocation) && gameStarted; }

Doppia personalita’

ConclusioniTest funzionali attorno al codice da refattorizzare

Refactoring ad ogni step e’ l’ideale

Se si parte con codice legacy usate le techniche viste

Conclusioni

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

Repository

http://github.com/sleli/BirdWatching

GRAZIE

@simonecasc@sleli

http://joind.in/4513