Abilitare l'agilità con il design semplice

Preview:

Citation preview

Abilitare l'agilità con il design

semplice

Mini IAD Vimercate 2017Gabriele Tondi - @racingDeveloper

public static Set<Coordinate> nextGeneration(Set<Coordinate> liveCells){ Set<Coordinate> nextGeneration = new HashSet<>();

for (Coordinate liveCell : liveCells) { Set<Coordinate> block = new HashSet<>(); for (int currentX1 = liveCell.x - 1; currentX1 <= liveCell.x + 1; currentX1++) { for (int currentY1 = liveCell.y - 1; currentY1 <= liveCell.y + 1; currentY1++) { block.add(new Coordinate(currentX1, currentY1));}}

block.remove(liveCell);

for (Coordinate currentCell : block) { int liveCellsCount = 0;

Set<Coordinate> currentBlock = new HashSet<>(); for (int currentX = currentCell.x - 1; currentX <= currentCell.x + 1; currentX++) { for (int currentY = currentCell.y - 1; currentY <= currentCell.y + 1; currentY++) { currentBlock.add(new Coordinate(currentX, currentY));}}

currentBlock.remove(currentCell); for (Coordinate coordinate : currentBlock) { if (liveCells.contains(coordinate)) { liveCellsCount++; }}

int aliveCells = liveCellsCount;

if (liveCells.contains(currentCell)) { if (aliveCells == 2 || aliveCells == 3) { nextGeneration.add(currentCell); }} else { if (aliveCells == 3) { nextGeneration.add(currentCell); }}}}

return nextGeneration;} A

public class World {

public World evolve() { Set<Cell> nextGeneration = nextGeneration = cells.stream() .map(cell -> cell .coordinate() .blockIncludeSelf()) .flatMap(coordinates -> coordinates .stream()) .map(coordinate -> evolveAt(coordinate)) .collect(toSet());

return new World(nextGeneration); }

private Cell evolveAt(Coordinate coordinate) { return cellAt(coordinate) .evolve(aliveNeighboursOf(coordinate)); }

private Cell cellAt(Coordinate coordinate) { return cells.stream() .filter(c -> c.isAt(coordinate)) .findFirst() .orElse(new DeadCell(coordinate)); }

public int aliveNeighboursOf(Coordinate c) { Set<Coordinate> block = c.block(); block.retainAll(aliveCellsCoordinates());

return block.size(); }

B

private Set<Cell> aliveCells() { return cells.stream() .filter(c -> c.isAlive()) .collect(toSet()); }

private Set<Coordinate> aliveCellsCoordinates() { return aliveCells().stream() .map(c -> c.coordinate()) .collect(toSet()); }}

public class AliveCell extends Cell { public Cell evolve(int aliveNeighboursCount){ // [...] }}

public class DeadCell extends Cell { public Cell evolve(int aliveNeighboursCount){ // [...] }}

public class Coordinate { public Set<Coordinate> block() { // [...] }

public Set<Coordinate> blockIncludeSelf(){ // [...] }}

Cosa cambia?

Struttura, ossia design.

Perché facciamo design?

Vanità?

Stile?

Arte?

NO!

Riduzione e controllo del costo del cambiamento

Cosa fareste?

tempo

cost

o ca

mbi

amen

to

Quanto ne sappiamo all'inizio?

Cosa fareste?

tempo

cost

o ca

mbi

amen

to

Quanto ne sappiamo?

design speculativo

assunzioni errateaumento costo cambiamento

per evitare di pagare un costo alto provo ad anticipare il

cambiamento

le speculazioni errate mi costano

per avere più possibilità di azzeccarci devo fare più

assunzioni!

Kent Beck spezza il ciclo a retroazione positiva

Prova a prevedere i cambiamenti ad un mese

...ad una settimana

...ad un giorno

...fino al momento stesso

4 regole del design semplice

Semplice? Il mio design è già semplice per me!

SempliceEtimologia:

lat. simplĭce(m), comp. di sem- ‘una volta’ e -plex, corradicale di plectĕre ‘intrecciare’ e plicāre ‘piegare’; propr. ‘intrecciato, piegato una sola volta’.

Definizione:

formato di un solo elemento

fonte: http://www.garzantilinguistica.it

FacileEtimologia:

lat. facĭle(m), deriv. di facĕre ‘fare’; propr. ‘che si fa, che è possibile fare’.

Definizione:

che si può fare senza difficoltà; agevole, comodo

che si ottiene con poca o senza fatica

fonte: http://www.garzantilinguistica.it

Facile è soggettivo

Semplice è oggettivo

Ci serve un metro per misurare in maniera oggettiva il design

Il design semplice:1. passa tutti i test

2. esprime l'intento

3. non contiene duplicazione

4. viene realizzato con il minor numero di elementi possibili

L'ordine conta

Il design semplice:

1. passa tutti i test

2. esprime l'intento

3. non contiene duplicazione

4. viene realizzato con il minor numero di elementi possibili

PRIORITÀ

+-

1. passa tutti i test

2. esprime l'intento

3. non contiene duplicazione

4. viene realizzato con il minor numero di elementi possibili

Devo poter verificare rapidamente i cambiamenti

Ah, quindi deve essere testabile!

1. passa tutti i test

2. esprime l'intento

3. non contiene duplicazione

4. viene realizzato con il minor numero di elementi possibili

Non è solo questione di naming (variabili, metodi privati etc)

Cos'è l'intento?

public List<Customer> selectWhereNameLike(String name)

public List<Customer> searchByName(String name)

Cos'è l'intento?

public void sendMailUsingSmtp(MailMessage message)

public void sendMessage(Message message)

Intento: cosa fa, non come lo fa!

Intento => Astrazione

Le astrazioni riducono l'accoppiamento

1. passa tutti i test

2. esprime l'intento

3. non contiene duplicazione

4. viene realizzato con il minor numero di elementi possibili

public static Set<Coordinate> nextGeneration(Set<Coordinate> liveCells){ Set<Coordinate> nextGeneration = new HashSet<>();

for (Coordinate liveCell : liveCells) { Set<Coordinate> block = new HashSet<>(); for (int currentX1 = liveCell.x - 1; currentX1 <= liveCell.x + 1; currentX1++) { for (int currentY1 = liveCell.y - 1; currentY1 <= liveCell.y + 1; currentY1++) { block.add(new Coordinate(currentX1, currentY1));}}

block.remove(liveCell);

for (Coordinate currentCell : block) { int liveCellsCount = 0;

Set<Coordinate> currentBlock = new HashSet<>(); for (int currentX = currentCell.x - 1; currentX <= currentCell.x + 1; currentX++) { for (int currentY = currentCell.y - 1; currentY <= currentCell.y + 1; currentY++) { currentBlock.add(new Coordinate(currentX, currentY));}}

currentBlock.remove(currentCell);

for (Coordinate coordinate : currentBlock) { if (liveCells.contains(coordinate)) { liveCellsCount++; }}

int aliveCells = liveCellsCount;

if (liveCells.contains(currentCell)) { if (aliveCells == 2 || aliveCells == 3) { nextGeneration.add(currentCell); }} else { if (aliveCells == 3) { nextGeneration.add(currentCell); }}}}

return nextGeneration;}

public static Set<Coordinate> nextGeneration(Set<Coordinate> liveCells){ Set<Coordinate> nextGeneration = new HashSet<>();

for (Coordinate liveCell : liveCells) { Set<Coordinate> block = new HashSet<>(); for (int currentX1 = liveCell.x - 1; currentX1 <= liveCell.x + 1; currentX1++) { for (int currentY1 = liveCell.y - 1; currentY1 <= liveCell.y + 1; currentY1++) { block.add(new Coordinate(currentX1, currentY1));}}

block.remove(liveCell);

for (Coordinate currentCell : block) { int liveCellsCount = 0;

Set<Coordinate> currentBlock = new HashSet<>(); for (int currentX = currentCell.x - 1; currentX <= currentCell.x + 1; currentX++) { for (int currentY = currentCell.y - 1; currentY <= currentCell.y + 1; currentY++) { currentBlock.add(new Coordinate(currentX, currentY));}}

currentBlock.remove(currentCell);

for (Coordinate coordinate : currentBlock) { if (liveCells.contains(coordinate)) { liveCellsCount++; }}

int aliveCells = liveCellsCount;

if (liveCells.contains(currentCell)) { if (aliveCells == 2 || aliveCells == 3) { nextGeneration.add(currentCell); }} else { if (aliveCells == 3) { nextGeneration.add(currentCell); }}}}

return nextGeneration;}

Cos'è la duplicazione?public Cell cellAt(int x, int y) { // [...]}

public Set<Cell> neighboursOf(int x, int y) { // [...]}

public bool isAliveAt(int x, int y) { // [...]}

Duplicazione di conoscenza, non di righe di codice

Riduco la duplicazione?public Cell cellAt(Coordinate coordinate) { // [...]}

public Set<Cell> neighboursOf(Coordinate coordinate) { // [...]}

public bool isAliveAt(Coordinate coordinate) { // [...]}

public class Coordinate { public int getX(); public int getY();}

Attrattore di comportamentipublic Cell cellAt(Coordinate coordinate) { // [...]}

public bool isAliveAt(Coordinate coordinate) { // [...]}

public class Coordinate { public Set<Coordinate> neighbours() { // [...] }}

Ridurre la duplicazione => Aumentare la coesione

É più importante ridurre la duplicazione o chiarire l'intento?

Le regole 2 e 3 lavorano in simbiosi

The Simple Design Dinamo (@jbrains)

1. passa tutti i test

2. esprime l'intento, non contiene duplicazione

3. viene realizzato con il minor numero di elementi possibili

Rimuoviamo il codice morto.

Facciamo emergere solo gli elementi necessari oggi

Il design è semplice se:1. posso verificarne il comportamento rapidamente

2. ha poco accoppiamento ed è molto coeso

3. è conciso

Costo del cambiamento (eXtreme Programming)

tempo

cost

o ca

mbi

amen

to

In conclusione

Per essere agili dobbiamo poter cambiare il nostro software con costi contenuti

Usiamo il design per ridurre e controllare il costo del cambiamento

Il design semplice ci indica la direzione

"Simplicity is prerequisite for reliability."Edsger W. Dijkstra

Per approfondire● K. Beck: eXtreme Programming Explained

● C. Haines: Understanding the Four Rules of Simple Designhttps://leanpub.com/4rulesofsimpledesign

● J.B.Rainsberger: Putting an age old battle to resthttp://blog.thecodewhisperer.com/permalink/putting-an-age-old-battle-to-rest

● M. Fowler: BeckDesignRuleshttps://martinfowler.com/bliki/BeckDesignRules.html

● C2 Wiki: Xp Simplicity Ruleshttp://wiki.c2.com/?XpSimplicityRules

Gabriele Tondi

@racingDeveloper info@gabrieletondi.it

Grazie!

Rimini, 25-27 Maggio 2017

http://www.socrates-conference.it

Domande?

Recommended