• Non ci sono risultati.

1.2 Hello World!

5.2.6 BlackJack

return true;

return next.chiediCarta();

} }

5.2.6 BlackJack

Gestire lo svolgimento di una partita con i tre sfidanti

Inizialmente dobbiamo inizializzare le strategie dei tre sfidanti. Ad esempio, con le strategie precedentemente definite:

mattia.setStrategia(new StrategiaBerserk(mattia, Strategia.STARE));

carlo.setStrategia(new StrategiaPrudente(carlo, new RandomStrategy(Strategia.STARE)));

violetta.setStrategia((new StrategiaGuardaBanco(violetta, banco, Strategia.STARE)));

Poi, dopo aver aggiunto i tre sfidanti alla lista, diamo le carte iniziali agli sfindanti e infine al banco.

sfidanti.add(carlo);

sfidanti.add(mattia);

sfidanti.add(violetta);

for (Sfidante sfidante : sfidanti) { sfidante.carteIniziali();

}banco.carteIniziali();

A questo punto, facciamo giocare ogni sfidante e alla fine di ognuno incrementiamo un contatore se verifichiamo che è sballato.

int sballati = 0;

for (Sfidante sfidante : sfidanti) { sfidante.gioca();

if (sfidante.isSballato()) sballati++;

}

Per ultima cosa, nel caso non tutti gli sfidanti siano sballati, gioca anche il banco.

if (sballati < sfidanti.size()) banco.gioca();

Rimane da fare un’ultima semplice modifica alle classi

Sfidante

e

Mazziere

in modo che le stampe successive producano un output significativo.

Infatti l’istruzione

System.out.println(sfidante);

richiama

sfidante.toString()

che non è stato ridefinito e quindi corrisponde ancora alla implementazione di default data a livello di Object: la stampa del nome della classe e del suo indirizzo in memoria.

È presente però già a livello di interfaccia

GiocatoreBJ

una implementazione di default del metodo

asString()

a cui possiamo delegare il compito.

Aggiungiamo quindi sia nella casse

Sfidante

che nella classe

Mazziere

il seguente codice:

5.2 Soluzioni

@Override

public String toString() { return asString();

}

6 Lab06: Musica Maestro!

Il punto di partenza di questo laboratorio è disponibile all’indirizzo: https://gitlab.com/

programmazione2/lab07-musica-maestro.

6.1 Esercizi

Obiettivo dell’esercizio è progettare e realizzare un insieme di classi che consentano di simulare un insieme di strumenti musicali, seguendo passo passo le specifiche di seguito riportate.

Prima di passare a uno step successivo consigliamo di assicurarsi con semplici test (usando JUnit) che quanto scritto funzioni.

6.1.1 Obiettivi Step 1

Implementare due diverse classi che rappresentano due diversi strumenti musicali:

Trumpet

e

Horn

. Entrambe le classi implementano l’interfaccia

MusicalInstrument

e rispondono alla chiamata del metodo

public @NotNull String play()

che restituisce nel primo caso la stringa"pepepe"e nel secondo la stringa"papapa".

Step 2

Implementare altre due classi

WaterGlass

e

IronRod

. Esse, pur non implementando l’interfaccia

MusicalInstrument

hanno un comportamento molto simile:

WaterGlass

possiede un metodo public @NotNull String tap()

che restituisce la stringa"diding";

IronRod

invece aderisce alla interfaccia

GermanPercussiveInstrument

e risponde quindi alla chiamata

public @NotNull String spiel() restituendo la stringa"tatang".

Step 3

Aggiungere la possibilità di usare istanze della classe

WaterGlass

e delle classi aderenti alla inter-faccia

GermanPercussiveInstrument

(e in particolare perciò della classe

IronRod

) come og-getti di tipo

MusicalInstrument

. Tale obiettivo deve essere raggiunto usando il design pattern denominato Adapter; più in dettaglio, è richiesta la realizzazione di due diversi Adapter:

Wate

rGlassInstrument

che realizza un Class adapter e

GermanMusicalInstrument

che realizza invece un Object adapter.

Step 4

Creare una classe che rappresenti un’orchestra di oggetti di tipo

MusicalInstrument

. Tale obiettivo deve essere raggiunto usando il design pattern denominato Composite; più in dettaglio, è richiesta la realizzazione di una classe

Orchestra

che deve implementare l’interfaccia

Musi

calInstrument

e deve rispondere all’invocazione del metodo

String

play()demandando la chiamata agli oggetti

MusicalInstrument

aggregati e inserendo degli a capi tra l’uno e l’altro.

Gli oggetti che compongono il gruppo orchestrale vengono passati all’interno di una

Collec

tion

direttamente al costruttore della classe

Orchestra

:

public Orchestra(@NotNull Collection<MusicalInstrument> instruments)

Step 5

È necessario avere di ogni strumento una versione che suoni fortissimo (cioè il cui suono sia scritto tutto in lettere maiuscole).

Per evitare un proliferare di nuove classi, tale obiettivo deve essere raggiunto usando il design pattern

Decorator

. Più in dettaglio, è richiesta la realizzazione di una classe

Fortissimo

che implementa l’interfaccia

MusicalInstrument

e decora un’istanza di

MusicalInstrument

in modo che il suono prodotto dall’elemento decorato risulti tutto in maiuscolo.

Step 6

È necessario avere di ogni strumento una versione che suoni lentamente (cioè le cui vocali vengano triplicate), e anche in questo caso deve essere realizzato tramite pattern

Decorator

.

Visto che ci sono chiaramente delle parti di codice in comune (specifiche del pattern) con la classe sviluppata al punto 6.1.1, prima di procedere alla scrittura di questa classe, si deve modificare la classe

Fortissimo

facendola ereditare da una nuova classe

AbstractDecorator

che appunto fattorizzi il codice comune.

È richiesta quindi la realizzazione di una classe

Slow

che estenda la classe

AbstractDecor

ator

e decora un’istanza di

MusicalInstrument

in modo che il suono prodotto dall’elemento decorato risulti con le vocali triplicate.

Nella classe astratta si consiglia di cercare un template di decorazione parametrico rispetto a un metodo astratto

public @NotNull String transformEffect(@NotNull String sound) che sarà l’unica cosa che dovranno realizzare i decoratori concreti.

6.2 Soluzioni

Step 1: Implementare due diverse classi che rappresentano due diversi strumenti: Trumpet e Horn. Entrambe le classi implementano l’interfaccia MusicalInstrument e rispondono alla chiamata del metodoString play()che restituisce nel primo caso la stringa"pepepe"e nel secondo la stringa"papapa".

Dopo aver creato con IntelliJ IDEA un nuovo progetto Java (di tipo Gradle, vedi Capitolo1.2), possiamo aggiungere le classi richieste in unpackage

it.unimi.di.prog2.music

. Il metodo

play

, che è comune ad entrambe, è ciò che definisce l’interfaccia

MusicalInstrument

, che

Trumpet

e

Horn

implementano in modi differenti. È utile procedere con un approccio test driven, cioè descrivendo prima l’obiettivo che vogliamo raggiungere con un test di unità (che

6.2 Soluzioni

inizialmente fallirà), scrivendo poi il codice che permette al test di passare e infine valutando se esistono possibilità di riorganizzare (con opportune azioni di refactoring) il codice in maniera più elegante o generale, conservando la corretta funzionalità del test. Questo ciclo che si ripete e che caratterizza l’approccio TDD, è ben riassunto dallo slogan “red-green-refactoring”, dove i colori ricordano l’esito del test come spesso vengono segnalati dagli IDEs.

Usando JUnit4 il primo test potrebbe essere:

package it.unimi.di.prog2.music;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class MusicTest {

@Test

public void testTrumpet() {

MusicalInstrument instrument = new Trumpet();

assertEquals("pepepe", instrument.play());

} }

Si noti che inizialmente nemmeno compila, perché i simboli

MusicalInstrument

,

Trumpet

e

play

non sono definiti. Possiamo però definirli velocemente facendoci aiutare dall’IDE (per esempio con Create interface; attenzione a crearle in src main java e non src test java).

La prima esecuzione del test è bene che provochi un fallimento (red): è la garanzia che stiamo introducendo una nuova funzionalità e non codice che non viene stimolato dal test. Per questo motivo

play

ritornanull.

// MusicalInstrument.java package it.unimi.di.prog2.music;

public interface MusicalInstrument { String play();

}

// Trumpet.java

package it.unimi.di.prog2.music;

public class Trumpet implements MusicalInstrument {

@Override

public String play() { return null;

} }

Ora possiamo lanciare il task check di Gradle e ottenere un test fallito.

expected:<pepepe> but was:<null>

Expected :pepepe Actual :null

Il fallimento si risolve facilmente (green!) e non si intravvedono possibilità di refactoring.

public class Trumpet implements MusicalInstrument {

@Override

public String play() { return "pepepe";

} }

Procediamo quindi con un nuovo test e un nuovo ciclo “red-green-refactoring”.

@Test

public void testHorn() {

MusicalInstrument instrument = new Horn();

assertEquals("papapa", instrument.play());

}

Ciò porta (dopo il fallimento iniziale) alla classe

Horn

funzionante.

package it.unimi.di.prog2.music;

public class Horn implements MusicalInstrument {

@Override

public String play() { return "papapa";

} }

Potremmo operare un piccolo refactoring nei test, per enfatizzare che gli oggetti

Trumpet

e

Horn

sono entrambi dei

MusicalInstrument

: i loro tipi, cioè, hanno una relazione is a con il tipo

MusicalInstrument

.

public class MusicTest {

private MusicalInstrument instrument;

@Test

public void testTrumpet() { instrument = new Trumpet();

assertEquals("pepepe", instrument.play());

}

@Test

public void testHorn() { instrument = new Horn();

assertEquals("papapa", instrument.play());

} }

Step 2: Implementare altre due classiWaterGlasseIronRod. Esse, pur non implementando l’interfacciaMusicalInstrumenthanno un comportamento molto simile: WaterGlasspossiede un metodo String tap() che restituisce la stringa "diding"; IronRod invece aderisce alla interfacciaGermanPercussiveInstrumente risponde quindi alla chiamataString spiel() restituendo la stringa"tatang".

Scriviamo subito un primo test.

@Test

public void testWaterGlass() {

6.2 Soluzioni

WaterGlass wg = new WaterGlass();

assertEquals("diding", wg.tap());

}

Per superarlo (non dimentichiamo di farlo prima fallire, però!) scriveremo la classe

WaterG

lass

.

package it.unimi.di.prog2.music;

public class WaterGlass { public String tap(){

return "diding";

} }

E per

IronRod

:

@Test

public void testIronRod() {

GermanPercussiveInstrument gpi = new IronRod();

assertEquals("tatang", gpi.spiel());

}

package it.unimi.di.prog2.music;

public class IronRod implements GermanPercussiveInstrument {

@Override

public String spiel() { return "tatang";

} }

Step 3: Aggiungere la possibilità di usare istanze della classeWaterGlasse delle classi ade-renti alla interfaccia GermanPercussiveInstrument (e in particolare perciò della classe Ir

onRod) come oggetti di tipoMusicalInstrument. Tale obiettivo deve essere raggiunto usando il design pattern denominato Adapter; più in dettaglio, è richiesta la realizzazione di due diversi Adapter: WaterGlassInstrument che realizza un Class adapter e GermanMusicalInstrum

ent che realizza invece un Object adapter.

Iniziamo a tradurre il primo obiettivo in un test.

@Test

public void testWaterGlassInstrument() { instrument = new WaterGlassInstrument();

assertEquals("diding", instrument.play());

}

Per soddisfare il test con un “Class adapter”,

WaterGlassInstrument

, oltre a implementare l’interfaccia

MusicalInstrument

, erediterà anche le funzionalità di

WaterGlass

e userà

tap()

per realizzare

play()

.

public class WaterGlassInstrument extends WaterGlass implements MusicalInstrument {

@Override

public String play() { return tap();

} }

GermanMusicalInstrument

è simile, ma invece di ereditare la funzionalità, delega a un oggetto di tipo

IronRod

la sonata.

@Test

public void testGermanMusicalInstrument() {

instrument = new GermanMusicalInstrument(new IronRod());

assertEquals("tatang", instrument.play());

}

public class GermanMusicalInstrument implements MusicalInstrument { private GermanPercussiveInstrument delegate;

public GermanMusicalInstrument(GermanPercussiveInstrument gpi) { delegate = gpi;

}

@Override

public String play() { return delegate.spiel();

} }

Si noti che il costruttore di

GermanMusicalInstrument

in generale richiede un

GermanPe

rcussiveInstrument

, non necessariamente un

IronRod

. Step 4, 5, 6:...

TBD

7 Lab07: Musica Maestro (con gli spettatori)!

Il punto di partenza di questo laboratorio è disponibile all’indirizzo: https://bitbucket.org/

prog2unimi/p2-lab07-2019.

7.1 Esercizi

Documenti correlati