Codice legacy, usciamo dal pantano!

63
Codice Legacy, usciamo dal pantano! Antonio Carpentieri (@acarpe) Stefano Leli (@sleli) giovedì 30 giugno 2011

Transcript of Codice legacy, usciamo dal pantano!

Page 1: Codice legacy, usciamo dal pantano!

Codice Legacy,usciamo dal pantano!

Antonio Carpentieri (@acarpe)Stefano Leli (@sleli)

giovedì 30 giugno 2011

Page 2: Codice legacy, usciamo dal pantano!

Regole

•Tempo a disposizione: 50 minuti

• Il codice va rifattorizzato per essere conforme ai principi S.O.L.I.D.

• I test possono essere modificati

giovedì 30 giugno 2011

Page 3: Codice legacy, usciamo dal pantano!

S.O.L.I.D. Principles

giovedì 30 giugno 2011

Page 4: Codice legacy, usciamo dal pantano!

giovedì 30 giugno 2011

Page 5: Codice legacy, usciamo dal pantano!

Single Responsibility Principle

“There should never be more than one reason for a class to change”

giovedì 30 giugno 2011

Page 6: Codice legacy, usciamo dal pantano!

giovedì 30 giugno 2011

Page 7: Codice legacy, usciamo dal pantano!

Interface Segregation Principle

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

“Clients should not be forced to depend upon interfaces that they do not use”

giovedì 30 giugno 2011

Page 8: Codice legacy, usciamo dal pantano!

giovedì 30 giugno 2011

Page 9: Codice legacy, usciamo dal pantano!

giovedì 30 giugno 2011

Page 10: Codice legacy, usciamo dal pantano!

giovedì 30 giugno 2011

Page 11: Codice legacy, usciamo dal pantano!

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

Page 12: Codice legacy, usciamo dal pantano!

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

giovedì 30 giugno 2011

Page 13: Codice legacy, usciamo dal pantano!

Il Gioco

•Simulatore di Bird Watching

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

giovedì 30 giugno 2011

Page 14: Codice legacy, usciamo dal pantano!

Le Classi

giovedì 30 giugno 2011

Page 15: Codice legacy, usciamo dal pantano!

Analisi del problema

giovedì 30 giugno 2011

Page 16: Codice legacy, usciamo dal pantano!

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

Il refactoring verterà sulla suddivizione di responsabilità (aumentare la coesione, diminuire l’accoppiamento)

•Maggiore riuso del codice•Maggiore robusteza del programma•Minor costo del cambiamento

giovedì 30 giugno 2011

Page 17: Codice legacy, usciamo dal pantano!

Passi di refactoring

giovedì 30 giugno 2011

Page 18: Codice legacy, usciamo dal pantano!

Duplicazione

giovedì 30 giugno 2011

Page 19: Codice legacy, usciamo dal pantano!

Duplicazione (semplice)

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

giovedì 30 giugno 2011

Page 20: Codice legacy, usciamo dal pantano!

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

Page 21: Codice legacy, usciamo dal pantano!

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

Page 22: Codice legacy, usciamo dal pantano!

Eliminiamola!

giovedì 30 giugno 2011

Page 23: Codice legacy, usciamo dal pantano!

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

giovedì 30 giugno 2011

Page 24: Codice legacy, usciamo dal pantano!

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

Page 25: Codice legacy, usciamo dal pantano!

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

Page 26: Codice legacy, usciamo dal pantano!

Diamo a Cesareciò che è di Cesare

giovedì 30 giugno 2011

Page 27: Codice legacy, usciamo dal pantano!

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

Page 28: Codice legacy, usciamo dal pantano!

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 29: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 30: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 31: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 32: Codice legacy, usciamo dal pantano!

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

}

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 33: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 34: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 35: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 36: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 37: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 38: Codice legacy, usciamo dal pantano!

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

Diamo a Cesare ciò che è di Cesare

giovedì 30 giugno 2011

Page 39: Codice legacy, usciamo dal pantano!

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

Page 40: Codice legacy, usciamo dal pantano!

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

giovedì 30 giugno 2011

Page 41: Codice legacy, usciamo dal pantano!

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

Page 42: Codice legacy, usciamo dal pantano!

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

Page 43: Codice legacy, usciamo dal pantano!

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

Page 44: Codice legacy, usciamo dal pantano!

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

Page 45: Codice legacy, usciamo dal pantano!

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

Page 46: Codice legacy, usciamo dal pantano!

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

Page 47: Codice legacy, usciamo dal pantano!

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

Page 48: Codice legacy, usciamo dal pantano!

Anche il metodo

public abstract class Bird { ... public void setHeight(int height) throws Exception{ this.height = height; }

...}

giovedì 30 giugno 2011

Page 49: Codice legacy, usciamo dal pantano!

Gli IF proprio non ci piacciono!

giovedì 30 giugno 2011

Page 50: Codice legacy, usciamo dal pantano!

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

Page 51: Codice legacy, usciamo dal pantano!

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 costruttore

giovedì 30 giugno 2011

Page 52: Codice legacy, usciamo dal pantano!

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

}

giovedì 30 giugno 2011

Page 53: Codice legacy, usciamo dal pantano!

Il risultato

public class GameField {...

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

...}

giovedì 30 giugno 2011

Page 54: Codice legacy, usciamo dal pantano!

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

Page 55: Codice legacy, usciamo dal pantano!

Così è meglio

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

giovedì 30 giugno 2011

Page 56: Codice legacy, usciamo dal pantano!

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

Page 57: Codice legacy, usciamo dal pantano!

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

Page 58: Codice legacy, usciamo dal pantano!

Di nuovo sulle responsabilità

giovedì 30 giugno 2011

Page 59: Codice legacy, usciamo dal pantano!

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

Page 60: Codice legacy, usciamo dal pantano!

BirdListpublic 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

Page 61: Codice legacy, usciamo dal pantano!

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

Page 62: Codice legacy, usciamo dal pantano!

I Repository

http://github.com/sleli/BirdWatching

giovedì 30 giugno 2011

Page 63: Codice legacy, usciamo dal pantano!

GRAZIE

Antonio Carpentieri (@acarpe)Stefano Leli (@sleli)

giovedì 30 giugno 2011