Codice legacy, usciamo dal pantano! @iad11
-
Upload
stefano-leli -
Category
Technology
-
view
448 -
download
2
description
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