1. Codice Legacy,
usciamo dal pantano!
Antonio Carpentieri (@acarpe)
Stefano Leli (@sleli)
giovedì 30 giugno 2011
2. 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
7. Interface Segregation
Principle
“Clients should not be forced to depend upon interfaces
that they do not use”
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();
}
giovedì 30 giugno 2011
11. 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
12. Bird Watching Game
http://github.com/sleli/BirdWatching
giovedì 30 giugno 2011
13. Il Gioco
• Simulatore di Bird Watching
• Una sorta di “battaglia navale”...
ma qui gli assi sono 3 (gli uccelli
volano)
giovedì 30 giugno 2011
16. Classe GameField
Responsabilità Collaborazioni
Gestisce la collezione di Birds
Dispone il campo da gioco
Valida il campo da gioco Classe Bird
Inizializza il campo (start)
Gestisce le logiche degli shot
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
23. Eliminiamola!
@Before
public void Setup() {
field = new GameField(new FieldSize(10,5,3));
}
giovedì 30 giugno 2011
24. 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
25. 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
26. Diamo a Cesare
ciò che è di Cesare
giovedì 30 giugno 2011
27. 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
28. Diamo a Cesare ciò che è di Cesare
giovedì 30 giugno 2011
29. Diamo a Cesare ciò che è di Cesare
bird.setLocation(location);
if (!(bird instanceof Chicken))
bird.setHeight(new Random().nextInt(this.depth));
giovedì 30 giugno 2011
30. Diamo a Cesare ciò che è di Cesare
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);
giovedì 30 giugno 2011
31. Diamo a Cesare ciò che è di Cesare
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);
giovedì 30 giugno 2011
32. Diamo a Cesare ciò che è di Cesare
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;
}
}
giovedì 30 giugno 2011
33. Diamo a Cesare ciò che è di Cesare
Location location = new Location(new Random().nextInt(fieldSize.width()),
new Random().nextInt(fieldSize.height()),
new Random().nextInt(fieldSize.depth()));
giovedì 30 giugno 2011
34. Diamo a Cesare ciò che è di Cesare
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();
}
giovedì 30 giugno 2011
35. Diamo a Cesare ciò che è di Cesare
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;
}
giovedì 30 giugno 2011
36. Diamo a Cesare ciò che è di Cesare
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
37. Diamo a Cesare ciò che è di Cesare
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;
}
giovedì 30 giugno 2011
38. Diamo a Cesare ciò che è di Cesare
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;
}
giovedì 30 giugno 2011
39. 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
40. Tell, don’t Ask
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;
}
giovedì 30 giugno 2011
41. 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
42. 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
43. 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
44. 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
45. 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
46. 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
47. 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
48. Anche il metodo
public abstract class Bird {
...
public void setHeight(int height) throws Exception{
this.height = height;
}
...
}
giovedì 30 giugno 2011
50. 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
51. 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
52. Il risultato
public class PlacingStrategyFactory { public interface IPlacingStrategy {
public IPlacingStrategy create(PlacingMode type, void place(List<Bird> birds);
FieldSize fieldSize) {
if (type == PlacingMode.Random) { }
return new RandomPlacingStrategy(fieldSize);
}
public class NullPlacingStrategy implements IPlacingStrategy {
return new NullPlacingStrategy();
} @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);
}
}
}
giovedì 30 giugno 2011
53. Il risultato
public class GameField {
...
public boolean startGame(PlacingMode pm) {
placingStrategyFactory.create(pm, fieldSize).place(birds);
gameStarted = isGameStarted();
return gameStarted;
}
...
}
giovedì 30 giugno 2011
54. 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
55. Così è meglio
private boolean isGameFieldValid()
{
boolean isValid = true;
for(Bird bird : birds) {
isValid = isValid && fieldSize.isWithinField(bird.getLocation());
}
return isValid;
}
giovedì 30 giugno 2011
56. 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
57. 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