Ingegneria del Software - Esercizi

Esercizi presi dai compiti in classe.

Warning

Questi esercizi sono stati svolti da me e da altri studenti, quindi potrebbero contenere errori. Se ne trovate, segnalateli.

Domande a risposta multipla

In generale, per convalidare dei requisiti non è possibile
  1. Eseguirli tramite test
  2. Servirsi di prototipi
  3. Usare modelli di analisi di consistenza
  4. Sviluppare dei test
3. Usare modelli di analisi di consistenza
Possiamo controllare che il software soddisfi i requisiti tramite test, facendo provare al client i vari prototipi e sviluppando dei test che controllino un determinato requisito.
La frase "Il sistema dovrà fornire prestazioni adeguate" è
  1. Un requisito molto importante
  2. Un requisito funzionale
  3. Un requisito non funzionale
  4. Un requisito interpretabile in tanti modi
3. Un requisito non funzionale
I requisiti non funzionali riguardano come il sistema svolge la sua funzione (es. affidabilità, efficienza, prestazioni, manutenibilità, etc.)
Nell'ingegneria dei requisiti è necessario
  1. Tutte le risposte
  2. Risolvere i conflitti fra i requisiti
  3. Revisionare i requisiti raccolti
  4. Stabilire le priorità
1. Tutte le risposte
Tutti i passi elencati vengono applicati nell'ingegneria dei requisiti.
La navigabilità nelle relazioni dei diagrammi UML indica
  1. Per un oggetto, le istanze di cui invoca i metodi
  2. Per una classe, quali altre classi conosce/usa
  3. Per una classe, quali oggetti usa
  4. Per una classe, le classi di cui è composta
2. Per una classe, quali altre classi conosce/usa
La freccia singola indica che una classe conosce un'altra classe e la utilizza in una qualche forma.
Le fasi generali del processo RUP sono
  1. Revisione requisiti, unione e uso componenti, produzione e pianificazione
  2. Analisi, progettazione, implementazione, test e revisione
  3. Avvio, elaborazione, costruzione, transizione, produzione
  4. Revisione, decisione, pianificazione, marketing
3. Avvio, elaborazione, costruzione, transizione, produzione
Descrizione del processo RUP.
Il processo a cascata non è adatto a
  1. Cambiare i requisiti in nessuna delle sue fasi
  2. Sviluppare sistemi realtime
  3. Cambiare requisiti durante le fasi della progettazione e codifica
  4. Produrre prototipi nella prima fase di sviluppo
3. Cambiare requisiti durante le fasi della progettazione e codifica
Nel processo a cascata, una volta stabiliti i requisiti, questi saranno considerati immutabili ed utilizzati per tutti gli step successivi, che si concludono con il rilascio del prodotto.
Quali delle seguenti attività n on è prevista in nessun processo di sviluppo
  1. Fornire al cliente gli sviluppatori di software
  2. Rilasciare al cliente incrementi di software a cadenza regolare
  3. Permettere al cliente di lavorare nella sede degli sviluppatori
  4. Valutare se la produzione del software è strategica
4. Valutare se la produzione del software è strategica
Questa è una valutazione che spetta al cliente, non agli sviluppatori.
Per il design pattern Observer, le sottoclassi di Observer
  1. Dovrebbero essere al massimo 20
  2. Dovrebbero avere almeno 2 istanze ciascuna
  3. Dovrebbero essere almeno 2
  4. Potrebbero avere una sola istanza ciascuna
4. Potrebbero avere una sola istanza ciascuna
Il pattern Observer permette ad un numero arbitrario e dinamico di Observer di effettuare un attach al Subject per essere informati di eventuali cambiamenti del suo stato.
Nel design pattern Mediator
  1. Colleague conosce la sua sottoclasse
  2. Mediator conosce la sua sottoclasse
  3. Colleague conosce la sottoclasse di Mediator
  4. Colleague conosce Mediator
4. Colleague conosce Mediator
È sufficiente che tutti i Colleague abbiano modo di utilizzare l'interfaccia offerta da Mediator per potersi scambiare i messaggi fra di loro. Il concrete mediator, invece, deve conoscere le implementazioni dei colleague.
I design pattern
  1. Forniscono una collezione di oggetti
  2. Descrivono una soluzione riusabile ad un problema noto
  3. Forniscono un modo per riutilizzare il codice
  4. Forniscono una gerarchia di classi
2. Descrivono una soluzione riusabile ad un problema noto
I design pattern sono soluzioni ben studiate che permettono di risolvere un problema noto in modo efficiente e riproducibile tramite una gerarchia di classi, ognuno con un ruolo ben definito.
Una variabile di un certo tipo T
  1. Può contenere solo istanze di T
  2. Può contenere istanze di T e di super-tipi di T
  3. Può contenere qualsiasi istanza
  4. Può contenere istanze di T e di sottotipi di T
4. Può contenere istanze di T e di sottotipi di T
Animal a = new Dog(); // Valido
Dog d = new Animal(); // Non valido
Nello sviluppo di sistemi OO è incoraggiato l'uso di dipendenze
  1. Da interfacce e non da classi
  2. Da librerie esterne
  3. Da classi e non da interfacce
  4. Da classi e oggetti
1. Da interfacce e non da classi
È sempre meglio dipendere da interfacce piuttosto che da classi concrete, in modo da poter cambiare l'implementazione senza dover modificare il codice che dipende da essa.
Tramite l'uso del Factory Method, il client ha il vantaggio di
  1. Poter creare gli oggetti a runtime
  2. Poter scegliere quale creator utilizzare
  3. Controllare quando usare e creare un'istanza
  4. Indicare solo quando ha bisogno di un'istanza, ma non quando questa deve essere creata
1. Poter creare gli oggetti a runtime
Il Factory Method permette di creare un'istanza di una classe senza doverne conoscere il tipo, in modo da poterla cambiare a runtime in base ai parametri forniti al creator.
Quali caratteristiche ha un sistema riflessivo
  1. Introspezione e intercettazione
  2. Elementi come classi, istanze e metodi
  3. Capacità di conoscere lo stato di un oggetto
  4. Concetti di ereditarietà e polimorfismo
1. Introspezione e intercettazione
Attraverso la riflessione è possibile conoscere dettagli a proposito di un oggetto quali i metodi che offre, le loro firme e i parametri che accettano. Questo permette, ad esempio, di intercettare le chiamate ai metodi e di modificarne il comportamento.
Nella soluzione proposta dal Mediator
  1. Una sottoclasse di Mediator chiama i metodi di alcune sottoclassi
  2. Mediator chiama i metodi di alcune superclassi
  3. Una sottoclasse di Mediator chiama i metodi di alcune superclassi
  4. Mediator chiama i metodi di alcune sottoclassi
1. Una sottoclasse di Mediator chiama i metodi di alcune sottoclassi
L'implementazione dell'interfaccia Mediator chiama i metodi dei ConcreteColleague, che sono sottoclassi di Colleague.
Nel design pattern State, i client conoscono
  1. Nessuna sottoclasse di State
  2. State
  3. Sottoclassi di State
  4. State e sottoclasse di State
1. Nessuna sottoclasse di State
L'utilizzo dello state è interno al Context. È solo con questo che il client interagisce.
Le relazioni di aggregazione in UML sono rappresentate da
  1. Rettangoli annidati
  2. Linee con triangoli alle estremità
  3. Linee
  4. Linee con un rombo all'estremità
4. Linee con un rombo all'estremità
Nel caso dell'aggregazione il rombo è vuoto. Se si trattasse di composizione, il rombo sarebbe pieno.
In UML, un diagramma delle attività mostra rettangoli
  1. Per attività, prodotti come ellissi e flussi con frecce
  2. Per attività, flussi con frecce
  3. Arrotondati per stati, flussi con frecce
  4. Arrotondati per attività, flussi con frecce
4. Arrotondati per attività, flussi con frecce La sintassi di un diagramma delle attività è la seguente:
  • Rettangoli arrotondati per le attività
  • Frecce per i flussi
  • Rombi per le ramificazioni
  • Cerchio pieno per lo stato iniziale

Explanation
Date le attività T1 da 15 giorni, T2 da 3 e T3 da 7, e sia T3 dipendente da T2; T1 e T2 possono essere eseguite in parallelo
  1. T2 può durare 22 giorni senza cambiare la durata complessiva
  2. Qualsiasi ritardo su T1, T2 o T3 si ripercuote sulla durata complessiva
  3. T1 può durare 5 giorni in più senza cambiare la durata complessiva
  4. T3 può durare 5 giorni in più senza cambiare la durata complessiva
4. T3 può durare 5 giorni in più senza cambiare la durata complessiva
T1 dura 15 giorni, T2 + T3 10 giorni. Se T3 dura 5 giorni durasse 5 giorni in può, entrambi i flussi durerebbero 15 giorni, che è comunque la durata complessiva del progetto.
Quando si dovrà lavorare a stretto contatto con il cliente, è consigliabile i/il processo/i
  1. Cascata
  2. Cascata, RUP
  3. RUP
  4. Spirale, XP
4. Spirale, XP
Sia il processo a spirale che XP prevedono un contatto continuo con il cliente per poter rispondere ai cambiamenti nei requisiti o eventuali feedback.
Il processo XP produce
  1. Poca documentazione, aderente allo standard UML
  2. Poca documentazione
  3. La documentazione che serve, a discrezione degli sviluppatori
  4. Molta documentazione
2. Poca documentazione
Nel processo XP la documentazione è ridotta al minimo, a favore di Story Cards.
Un metodo di tipo synchronized
  1. Consente di iniziare l'esecuzione più velocemente
  2. Una volta iniziato, procede fino alla fine
  3. Aumenta il parallelismo del sistema
  4. Permette l'esecuzione di un solo thread all'interno dell'oggetto
4. Permette l'esecuzione di un solo thread all'interno dell'oggetto
Un metodo di tipo synchronized è un metodo che può essere eseguito da un solo thread alla volta. Implementa i monitor di Hoare.
Un'interfaccia X, ovvero public interface X
  1. Serve a runtime ad allocare memoria per una variabile
  2. Può contenere metodi implementati
  3. Ha lo scopo di definire un tipo
  4. Ha lo scopo di definire un sottotipo
3. Ha lo scopo di definire un tipo
Un'interfaccia definisce un tipo che sarà poi implementato da altre classi.
Un'interfaccia X, ovvero public interface X
  1. Contiene l'implementazione di algoritmi
  2. Può essere istanziata
  3. Permette alle classi che la usano di non legarsi ad una implementazione
  4. Contiene implementazioni di algoritmi non completi
3. Permette alle classi che la usano di non legarsi ad una implementazione
Utilizzando un'interfaccia, avviene un accoppiamento più lasco fra le classi che la usano, che non devono necessariamente conoscere le classe che la implementano.
I design pattern strutturali permettono
  1. Creare oggetti
  2. Comporre oggetti ma non classi
  3. Comporre classi ma non oggetti
  4. Comporre classi e oggetti
4. Comporre classi e oggetti
I design patter strutturali permettono di comporre classi e oggetti al fine di realizzare un sistema più complesso. Esempi sono l'Adapter, il Composite, il Bridge, il Decorator.
La legge di Lehman sul cambiamento continuo dice
  1. Poiché i sistemi sono aggiornabili, i cambiamenti sono continui
  2. I sistemi dovrebbero essere continuamente cambiati per rimanere utili
  3. Poiché i sistemi mutano continuamente degradano le loro prestazioni
  4. I sistemi prima o poi dovranno essere sostituiti da altri
2. I sistemi dovrebbero essere continuamente cambiati per rimanere utili
Una delle caratteristiche che distingue il software da altri prodotti è la sua modificabilità. Al fine di impedire l'obsolescenza, il software deve essere continuamente aggiornato e mantenuto.
public Libro getLibro() { return b.getLib(); }
Il frammento di codice...
  1. è compilabile solo se getLib() restituisce un'istanza di Libro
  2. è compilabile solo se b è inizializzato
  3. sta svolgendo il ruolo di Factory method
  4. è inutile
1. è compilabile solo se getLib() restituisce un'istanza di Libro
Se getLib() non restituisce un oggetto di tipo Libro, il compilatore restituirà un errore. Il codice è compilabile nel caso in cui b sia definito ma non inizializzato, ma a runtime verrà lanciata una NullPointerException.
I requisiti utente sono più spesso descritti tramite
  1. Linguaggio naturale, tabelle e diagrammi
  2. Casi d'uso soltanto
  3. Linguaggio formale
  4. Diagrammi soltanto
1. Linguaggio naturale, tabelle e diagrammi
Vi sono vari metodi per descrivere i requisiti utente. Un cliente potrebbe usarne più di uno, partendo da un linguaggio naturale, per poi passare ad una versione più formale tramite tabelle e diagrammi.
Cosa significa che i requisiti devono essere completi e consistenti?
  1. Ogni requisito ha un modello di descrizione
  2. Ogni requisito deve avere testo e diagrammi
  3. Tutto ciò che è descritto è testabile, ma non tutto è descritto
  4. Bisogna descrivere tutto senza contraddizioni
4. Bisogna descrivere tutto senza contraddizioni
Nella fase di analisi dei requisiti, è importante che questi descrivano il comportamento del software nella sua interezza e che non ci siano requisiti in conflitto tra loro.
Uno dei vantaggi della riflessione computazionale consiste nella possibilità di
  1. istanziare classi conosciute a compile time
  2. invocare metodi conosciuti a compile time
  3. invocare metodi conosciuti non conosciuti a design time
  4. invocare metodi conosciuti non conosciuti a runtime
3. invocare metodi conosciuti non conosciuti a design time
Tramite la riflessione si ha la possibilità di scorrere ed invocare a runtime i metodi di un oggetto senza conoscerli a priori.
Il metodo newInstance() è fornito dalla classe
  1. Implementata dal programmatore dell'applicazione
  2. Method
  3. Object
  4. Class
4. Class
Si tratta di un metodo della class Class che permette di creare una nuova istanza della classe rappresentata dall'oggetto Class. È deprecated a partire da Java 9.
Nel design pattern Observer si desidera ottenere
  1. L'indipendenza tra un oggetto ed il numero e il tipo di osservatori
  2. Oggetti che non interagiscono
  3. Due oggetti strettamente accoppiati
  4. Due oggetti lascamente accoppiati
1. L'indipendenza tra un oggetto ed il numero e il tipo di osservatori
Nel pattern observer, il Subject mantiene una lista di Observer, che si possono registrare e deregistrare a runtime.
Nel design pattern State, i client conoscono
  1. ConcreteStateA e Context
  2. ConcreteStateA, ConcreteStateB, etc.
  3. State e Context
  4. Context
4. Context
Il client interagisce con il Context. Al suo interno, questo mantiene uno State che determina il suo comportamento e può cambiare a runtime.
Date le attività T1: 15g, T2: 5g e T3: 5g. T3 dipende da T2 e T1. T1 e T2 eseguono in parallelo. Il percorso critico è di
  1. 15 giorni
  2. 30 giorni
  3. 25 giorni
  4. 20 giorni
4. 20 giorni
T1 (15) -> T3 (5)
Con correttezza del software si intende
  1. Facilità nell'apportare cambiamenti per soddisfare nuove esigenze
  2. Soddisfare il 90% degli utenti
  3. Aderenza allo scopo, conformità alle specifiche
  4. Evitare sprechi di memoria e del processore
3. Aderenza allo scopo, conformità alle specifiche
Un software è corretto se soddisfa le specifiche e se non ha difetti individuabili dai test.
In UML, l'implementazione di un'interfaccia è rappresentata da
  1. Linee tratteggiate con un rombo all'estremità
  2. Linee continue con un triangolo vuoto all'estremità
  3. Linee tratteggiate con un triangolo vuoto all'estremità
  4. Linee senza frecce
3. Linee tratteggiate con un triangolo vuoto all'estremità
Nello schema UML, l'implementazione di un'interfaccia è rappresentata da una linea tratteggiata con un triangolo vuoto all'estremità che parte dalla classe concreta e punta verso l'interfaccia.
Un diagramma di interazione (o collaborazione) UML è
  1. Un grafo che mostra le chiamate tra i metodi delle istanze
  2. Un grafo che mostra la navigabilità tra le classi
  3. Un albero che mostra l'ereditarietà tra le classi
  4. Un grafo che mostra l'ereditarietà e istanziazioni fra classi e oggetti
1. Un grafo che mostra le chiamate tra i metodi delle istanze
Il diagramma di interazione mostra le chiamate tra i metodi delle istanze che avvengono all'interno del software.
Nella soluzione proposta dal pattern Class Adapter
  1. L'adapter contiene un riferimento all'oggetto Adaptee
  2. Il client contiene un riferimento all'oggetto Adaptee
  3. L'Adapter eredita l'Adaptee
  4. L'Adaptee contiene un riferimento all'oggetto Adapter
3. L'Adapter eredita l'Adaptee
Nella versione Class Adapter, l'Adapter eredita l'Adaptee. Nella versione Object Adapter, l'Adapter contiene un riferimento all'Adaptee.
La soluzione suggerita dal design patter Class Adapter
  1. Adatta una classe modificandone i metodi
  2. Elimina l'uso di una istanza all'interno di Adapter
  3. Non si può implementare in Java
  4. Istanzia un Adaptee all'interno dell'Adapter
2. Elimina l'uso di una istanza all'interno di Adapter
Estendendo direttamente l'Adaptee, l'Adapter non ha bisogno di contenere un riferimento all'Adaptee, in quanto è esso stesso un Adaptee.
I design pattern
  1. Descrivono il processo di riuso del codice
  2. Sono una collezione di artefatti del programmatore
  3. Descrivono una soluzione riusabile ad un problema noto
  4. Forniscono una organizzazione gerarchica delle classi
3. Descrivono una soluzione riusabile ad un problema noto
I design pattern sono una raccolta di soluzioni ben studiate a problemi noti, che possono essere riutilizzate in contesti diversi.
Un design pattern è
  1. Un problema ricorrente
  2. Una soluzione collaudata ma generica
  3. Una soluzione non collaudata
  4. Un utile supporto per il riutilizzo del codice
2. Una soluzione collaudata ma generica
Ogni design pattern rappresenta una soluzione collaudata ad un problema ricorrente, ma è generica in quanto può essere applicata in contesti diversi, fintanto che l'intento del pattern è rispettato.
In un piano per le attività di un progetto troviamo
  1. Solo costi del personale
  2. Solo diagrammi che indicano il flusso di dati
  3. Diagrammi dei casi d'uso, delle classi e degli stati
  4. Tabelle, diagrammi a barre e reti di attività
4. Tabelle, diagrammi a barre e reti di attività
Articolo
Il design pattern Adapter permette a certe classi di interagire perché
  1. Rende note le istanze di classi presenti
  2. Elimina le incompatibilità tra le interfacce
  3. Passa le chiamate tra classi implementate in linguaggi diversi
  4. Tiene traccia dei nomi delle classi disponibili
2. Elimina le incompatibilità tra le interfacce
La classe Adapter permette al client di chiamare i metodi dell'interfaccia Target, traducendoli poi in chiamate ai metodi dell'interfaccia Adaptee.
È consigliabile usare il Facade quando
  1. Si vogliono far dipendere i client solo da interfacce
  2. Si vuole nascondere la complessità di un sottosistema
  3. Si hanno classi molto accoppiate
  4. Si hanno classi con funzionalità simili
2. Si vuole nascondere la complessità di un sottosistema
Il facade offre un'interfaccia di livello più alto al client, nascondendo una serie di chiamate a classi che compongono un sistema complesso.
Un documento dei requisiti ben scritto mirerà a fornire
  1. Valutazioni sul codice che deriverà dagli stessi requisiti
  2. Informazioni da cui derivare la progettazione del sistema
  3. Dettagli sulla progettazione del sistema
  4. Dati sul guadagno che si trarrà dopo la produzione
2. Informazioni da cui derivare la progettazione del sistema
Il documento dei requisiti si produce nella fase di analisi, e contiene informazioni che permettono di passare alla fase di progettazione.
Nella soluzione del design pattern State, ogni Concrete State
  1. Definisce l'interfaccia che il client usa
  2. Definisce l'interfaccia associata ad uno stato
  3. Mantiene un'istanza di State
  4. Implementa un comportamento
4. Implementa un comportamento
Ogni Concrete State definisce un comportamento associato ad uno stato del Context, e può anche essere responsabile di cambiare lo stato del Context.
Per il pattern Observer, cosa è interessante osservare?
  1. Il comportamento del Subject
  2. Il comportamento del Concrete Observer
  3. Lo stato del Concrete Observer
  4. Lo stato del Concrete Subject
4. Lo stato del Concrete Subject
Il Concrete Observer è interessato allo stato del Concrete Subject, e può essere notificato dal Concrete Subject quando questo cambia.
La coesione di una classe è
  1. Alta se la classe ha pochi metodi e attributi
  2. Alta se tutti i metodi contribuiscono ad implementare un singolo compito
  3. Alta se il codice della classe è facile da comprendere
  4. Tanto più alta quanto più alta è la coesione di ogni metodo
2. Alta se tutti i metodi contribuiscono ad implementare un singolo compito
La coesione di una classe è alta se tutti i metodi contribuiscono ad implementare un singolo compito, e bassa se i metodi implementano compiti diversi.
La legge di Lehman è pensata per
  1. software di grandi dimensioni
  2. software di piccole dimensioni
  3. prodotti COIST
  4. piccole aziende di software
1. software di grandi dimensioni
La legge di Lehman è pensata per software di grandi dimensioni, e afferma che un software deve essere modificato per rimanere utile.
Sia la classe Agent un Singleton. Si abbia la linea di codice: Agent a = Agent.getInstance()
  1. Verrà creata una volta sola l'istanza di Agent, a prescindere dal numero di esecuzioni
  2. Viene restituito un errore a compile time
  3. Viene restituito un errore a run time appena si incontra per la seconda volta tale linea di codice
  4. Verrà creata una istanza di Agent ogni volta che viene incontrata la suddetta linea di codice
1. Verrà creata una volta sola l'istanza di Agent, a prescindere dal numero di esecuzioni
Il design pattern singleton assicura che venga creata una sola istanza di una classe, e che questa sia accessibile globalmente, tramite un metodo statico. Il costruttore della classe è privato.
Per il Factory Method
  1. La classe Creator ha un metodo che ritorna ConcreteProduct
  2. La classe ConcreteCreator ha un metodo che ritorna Product
  3. La classe ConcreteProduct ha un metodo che ritorna Creator
  4. La classe ConcreteCreator ha un metodo che ritorna ConcreteProduct
2. La classe ConcreteCreator ha un metodo che ritorna Product
Con il factory method, il client si disinteressa dell'instanziazione di un product, delegando il compito al ConcreteCreator. Agli occhi del client, gli verrà restituito un Product. Sarà compito del ConcreteCreator scegliere ed instanziare il ConcreteProduct corretto.
Fissata un'interfaccia, il design pattern Decorator
  1. Lascia non fissati alcuni nomi dei metodi pubblici delle classi
  2. Fornisce una lista di istanze di classi che la implementano
  3. Richiede di implementare metodi con lo stesso nome ma in classi diverse
  4. Richiede classi client diverse fra loro
3. Richiede di implementare metodi con lo stesso nome ma in classi diverse
Nel design pattern Decorator, si ha un'interfaccia che definisce un metodo pubblico. Le classi ConcreteDecorator implementano l'interfaccia e contengono un riferimento ad Component, aggiungendo funzionalità al metodo, e finendo poi per richiamare il metodo del Component stesso.
In genere, quale dei seguenti prende decisioni su instanziazione di classi?
  1. Una abstract class
  2. Mediator
  3. Singleton
  4. Observer
3. Singleton
Il singleton è un pattern creazionale, che assicura che venga creata una sola istanza di una classe, e che questa sia accessibile globalmente, tramite un metodo statico.
In un codice che utilizza il pattern Bridge vengono creati 3 ConcreteImplementor e 4 Refined Abstraction. Se non utilizzassi il pattern, e volessi mantenere tutte le possibili combinazioni, quante classi dovrei creare?
  1. 7
  2. 12
  3. 9
  4. 16
2. 12
Per poter combinare tutti i ConcreteImplementor con tutti i Refined Abstraction, dovrei creare 3*4=12 classi.
Quale fra questi è un vantaggio che ottengo utilizzando il pattern Adapter
  1. Nascondere la complessità di un sotto-sistema
  2. Poter cambiare delle interfacce senza dover modificare il client
  3. Poter far interagire due server web diversi
  4. Aggiungere funzionalità ad una classe più semplice
2. Poter cambiare delle interfacce senza dover modificare il client
Utilizzare il pattern Adapter slega il client dall'implementazione di un'interfaccia esterna. Volendola cambiare, sarà sufficiente aggiornare l'Adapter, senza dover modificare il client.
Quale pattern è particolarmente indicato per rappresentare delle strutture ricorsive ed operare su esse?
  1. Chain of Responsibility
  2. Decorator
  3. Composite
  4. Bridge
3. Composite
Il Composite è un pattern strutturale, che permette di utilizzare indistintamente un oggetto singolo o un insieme di oggetti tramite un'interfaccia comune. Nell'interfaccia sarà definito un metodo implementato in modo diverso a seconda che l'oggetto sia singolo o composto (implementazione ricorsiva).
Un negozio vende una collezione di blocchi. Questi possono essere di metallo o di legno, dipinti, graffiati o patinati. Talvolta possono addirittura essere radioattivi o magnetizzati.
Quale design pattern permetterebbe di introdurre nuove tipologie di blocchi senza modificare codice già esistente?
  1. Observer
  2. State
  3. Bridge
  4. Decorator
3. Bridge
Utilizzando il Bridge, abstraction (Blocco) e implementor (Caratteristica) sono separate. Diviene semplice aggiungere un ulteriore concrete implementor (ad esempio, blocchi di vetro) o refined abstraction senza dover modificare il codice già esistente.
In un gioco il personaggio protagonista acquisisce sempre più potenziamenti man mano che prosegue. Questi sbloccano nuove abilità, permettendogli di compiere azioni che gli erano precluse.
Che pattern si può utilizzare per gestire il comportamento del personaggio?

  1. State
  2. Composite
  3. Bridge
  4. Observer
1. State
Durante la sua avventura, lo stato del personaggio cambia, e con esso le sue abilità e azioni possibili.
Limita l'istanziazione di una classe ad un solo oggetto
  1. Facade
  2. Observer
  3. Singleton
  4. NullObject
3. Singleton
Il singleton è un pattern creazionale, che assicura che venga creata una sola istanza di una classe, e che questa sia accessibile globalmente, tramite un metodo statico.
Aggiunge delle funzionalità ad un oggetto in maniera dinamica
  1. Decorator
  2. Adapter
  3. Facade
  4. Composite
1. Decorator
Il decorator è un pattern strutturale, che permette di aggiungere funzionalità ad un oggetto in maniera dinamica, wrappandolo in un altro oggetto che implementa la stessa interfaccia.
Viene utilizzato un oggetto per incapsulare tutte le informazioni necessarie a richiamare un metodo successivamente
  1. Bridge
  2. Prototype
  3. Strategy
  4. Command
4. Command
Il command è un pattern comportamentale. Si introduce una classe Command che, quando eseguito tramite il metodo execute(), richiama i metodi del receiver.
Il comportamento dell'algoritmo può essere selezionato a runtime
  1. Factory Method
  2. Strategy
  3. State
  4. Decorator
2. Strategy
Il pattern Strategy permette di selezionare a runtime il comportamento di un algoritmo, separando l'implementazione dell'algoritmo dall'oggetto che lo utilizza, che poi lo incapsulerà al suo interno.

Domande a risposta aperta

Disegnare e spiegare i passi (o attività) della gestione dei requisiti in un processo tipo quello a cascata
  1. Studio di fattibilità
  2. Analisi dei requisiti
  3. Specifica dei requisiti
  4. Convalida requisiti
public class Buffer {
  private int box;
  private boolean full = false;
  public mod1 int get() throws InterruptedException {
    while (!full) metodo1();
    full = false; notifyAll(); return box;
  }
  public mod2 void put(int value) throws InterruptedException {
    while (full) metodo2();
    box = value; full = true; metodo3();
  }
}
Quali sono dei nomi appropriati per metodo1, metodo2 e metodo3?
Che modificatori sono mod1 e mod2
metodo1, metodo2 -> wait
metodo3 -> notifyAll
mod1, mod2 -> synchronized

Produrre il design ad oggetti corrispondente ai seguenti requisiti:

  • Su richiesta dell'utente deve essere calcolato il costo complessivo degli ordini fatti dal cliente in un mese
  • Per gli ordini in un mese dovranno essere fornite le categorie a cui appartengono ciascuno dei prodotto acquistati
public class Order {
    String product;
    int cost;
    String category;
    String month;
    String client;
    // getters and setters
}
class OrderManager {
    List<Order> orders = new ArrayList<>();
    public int getCostInMonthForClient(String month, String client) {
        return orders.stream()
                .filter(order -> order.month.equals(month))
                .filter(order -> order.client.equals(client))
                .mapToInt(order -> order.cost)
                .sum();
    }
    public List<String> getCategoriesInMong(String month) {
        return orders.stream()
                .filter(order -> order.month.equals(month))
                .map(order -> order.category)
                .distinct()
                .collect(Collectors.toList());
    }
}

Design pattern

Esercizio 1

public interface IDataSource {
 public String getNomeCompleto();
 public int getEta();
}
public class Info {
  private String nome;
  private String cognome;
  private Date dataDiNascita;
  public Info(String nome, String cognome, Date dataDiNascita) {
    this.nome = nome; this.cognome = cognome; this.dataDiNascita = dataDiNascita;
  }
  public String getNome() { return nome;}
  public String getCognome() { return cognome;}
  public Date getDataDiNascita() { return dataDiNascita;}
}

Domanda 1

Il client vuole ottenere dei dati di un utente utilizzando l'interfaccia IDataSource. Quale pattern potrebbe utilizzare? Scrivere tutte le componenti mancanti necessarie per realizzare il pattern Il design pattern Adapter
public class InfoAdapter implements IDataSource {
  private Info info;
  public InfoAdapter(Info info) { this.info = info; }
  public String getNomeCompleto() {
    return info.getNome() + " " + info.getCognome();
  }
  public int getEta() {
    return LocalDate.now().getYear() - info.getDataDiNascita().getYear();
  }
}

Domanda 2

Utilizzare una variante del pattern per risolvere lo stesso problema.

public class InfoAdapter extends Info implements IDataSource {
  public InfoAdapter(String nome, String cognome, Date dataDiNascita) {
    super(nome, cognome, dataDiNascita);
  }
  public String getNomeCompleto() {
    return getNome() + " " + getCognome();
  }
  public int getEta() {
    return LocalDate.now().getYear() - getDataDiNascita().getYear();
  }
}

Esercizio 2

public interface Auto {
  public String getTipo();
  public int getPeso();
  public float getDistanza(int t);
}
public interface Motore {
  public int getPotenza();
}
public class Berlina implements Auto {
  private Motore m;
  public Berlina(Motore x) { m = x; }
  public String getTipo() { return "Berlina"; }
  public int getPeso() { return 800; }
  public float getDistanza(int t) {
    return (float) t * t * m.getPotenza() / getPeso();
  }
}
public class Fire implements Motore {
  public int getPotenza() { return 1000; }
}

Domanda 1

Qual è il design pattern implementato e il ruolo svolto dalle classi/interfacce Auto, Motore, Berlina e Fire? Il pattern implementato è il Bridge. Auto è l'abstraction, Berlina è la refined abstraction, Motore è l'implementor e Fire è la concrete implementor.

Domanda 2

Implementare la classe C1 che istanzia le classi Berlina e Fire in modo opportuno.

public class C1 {
  public static Auto getNewAuto() {
    Motore m = new Fire();
    Auto a = new Berlina(m);
  }
}

Domanda 3

Implementare la classe C2 che chiama i metodi definiti in Auto.

public class C2 {
  public static main(String args[]) {
    Auto auto = C1.getNewAuto();
    System.out.println(auto.getTipo());
    System.out.println(auto.getPeso());
    System.out.println(auto.getDistanza(10));
  }
}

Domanda 4

Disegnare il diagramma UML delle classi per il codice mostrato e per le classi C1 e C2 indicate

Loading diagram...

Domanda 5

Disegnare il diagramma UML di sequenza per il codice mostrato e per le classi C1 e C2 indicate

Loading diagram...

Esercizio 3

public interface Esame {
  public void prenota();
  public void registra();
}
public class Nuovo implements Esame {
  public void prenota() { System.out.println("Presentazione Ok"); }
  public void registra() { System.out.println("Impossibile registrare l’esame"); }
}
public class Prenotato implements Esame {
  public void prenota() { System.out.println("Impossibile prenotare l'esame"); }
  public void registra() { System.out.println("Registrazione Ok"); }
}

Domanda 1

Completare l'implementazione del design Pattern State con almeno due classi.

// Context
public class Appello {
  private Esame state = new Nuovo(this);
  public void prenota() {
    this.state = this.state.prenota();
  }
  public void registra() {
    this.state = this.state.registra();
  }
}
// Concrete State 1
public class Nuovo implements Esame {
  private Appello appello;
  public Nuovo(Appello appello) { this.appello = appello; }
  public Esame prenota() {
    System.out.println("Presentazione Ok");
    return new Prenotato(appello);
  }
  public Esame registra() {
    System.out.println("Non è possibile registrare l’esame");
    return this;
  }
}
// Concrete State 2
public class Prenotato implements Esame {
  private Appello appello;
  public Prenotato(Appello appello) { this.appello = appello; }
  public Esame prenota() {
    System.out.println("Non è possibile prenotare l’esame");
    return this;
  }
  public Esame registra() {
    System.out.println("Registrazione Ok");
    return new Registrato(appello);
  }
}
// Concrete State 3
public class Registrato implements Esame {
  private Appello appello;
  public Registrato(Appello appello) { this.appello = appello; }
  public Esame prenota() {
    System.out.println("Non è possibile prenotare l’esame");
    return this;
  }
  public Esame registra() {
    System.out.println("Non è possibile registrare l’esame");
    return this;
  }
}

Domanda 2

Inserire in una delle classi la memorizzazione del nome della materia e della data dell'esame.

public class Appello {
  // ...
  private String materia;
  private LocalDate date;
  public Appello(String materia, LocalDate date) {
    this.materia = materia;
    this.date = date;
  }
  public String getMateria() { return this.materia; }
  public LocalDate getDate() { return this.data; }
  // ...
}
public class Nuovo implements Esame {
  // ...
  public Esame prenota() {
    if (appello.getDate().before(LocalDate.now())) {
      System.out.println("Presentazione Ok");
      return new Prenotato(appello);
    }
    System.out.println("Non è possibile prenotare l’esame");
    return this;
  }
  // ...
}

Domanda 3

Dare il nome dei ruoli per tutte le classi e interfacce del design Pattern.
  • Appello: Context
  • Esame: State
  • Nuovo: Concrete State 1
  • Prenotato: Concrete State 2
  • Registrato: Concrete State 3

Domanda 4

Disegnare il diagramma UML delle classi.

Loading diagram...

Domanda 5

Disegnare il diagramma UML di sequenza illustrando l'esecuzione a partire da una classe appropriata.

Loading diagram...

Esercizio 4

public class Student {
  private String nome = "Al";
  private int aperti = 0;
  private Modulo[] e = new Modulo[10];
  private int crediti = 0;
  public void inizia() {
    if (aperti < e.length) { e[aperti++] = new Modulo(); }
  }
  public void completa() {
    if (aperti > 0) { crediti += e[--aperti].getCrediti(); }
  }
}
public class Modulo {
  private boolean superato = false;
  public int getCrediti() { return 3; }
  public boolean superato() { return superato; }
  public void setSuperato() { superato = true; }
}

Domanda 1

Cambiare la classe Modulo in modo da far svolgere il ruolo di Concrete product del design pattern Factory Method.

public interface Modulo {
  int getCrediti();
  boolean superato();
}
public class ModuloSuperato extends Modulo {
  public int getCrediti() { return 3; }
  public boolean superato() { return true; }
}
public class ModuloIniziato extends Modulo {
  public int getCrediti() { return 0; }
  public boolean superato() { return false; }
}

Domanda 2

Implementare una classe che instanzia il Concrete product in modo appropriato al design pattern.

public class ModuloFactory {
  public static Modulo create(boolean isSuperato) {
    return isSuperato ? new ModuloSuperato() : new ModuloIniziato();
  }
}

Domanda 3

Cambiare la classe studente in modo da usare le classi ricavate precedentemente.

public class Student {
  // ...
  public void inizia() {
    if (aperti < e.length) { e[aperti++] = ModuloFactory.create(false); }
  }
  public void completa() {
    if (aperti > 0) {
      e[aperti] = ModuloFactory.create(true);
      crediti += e[aperti].getCrediti();
      aperti--;
    }
  }
}

Domanda 4

Disegnare il diagramma UML delle classi.

Loading diagram...

Domanda 5

Disegnare il diagramma UML di sequenza illustrando l'esecuzione a partire da una classe appropriata.

Loading diagram...

Stream

Soluzioni

Data una lista di persone, trovare i nomi dei programmatori con età minore di 30 anni.

public record Persona(String name, int age, String role) {}

List<Persona> l = List.of(new Persona("Kent", 29, "CTO"),
                          new Persona("Luigi", 25, "Programmer"),
                          new Persona("Andrea", 26, "GrLeader"),
                          new Persona("Sofia", 26, "Programmer"),
                          new Persona("Alfio", 63, "Programmer"));
// ...
// result = ["Luigi", "Sofia"]

Data una lista di istanze di Persona trovare i diversi ruoli.

public record Persona(String name, int age, String role) {}

List<Persona> l = List.of(new Persona("Kent", 29, "CTO"),
                          new Persona("Luigi", 25, "Programmer"),
                          new Persona("Andrea", 26, "GrLeader"),
                          new Persona("Sofia", 26, "Programmer"));
// ...
// result = ["CTO", "Programmer", "GrLeader"]

Data una lista di stringhe, produrre una lista che contiene solo le stringhe che cominciano con un certo prefisso noto.

List<String> l = List.of("author", "auto",
                          "autocorrect", "begin",
                          "big", "bigger", "biggish");
// ...
// se prefisso = "au", result = ["author", "auto", "autocorrect"]

Data una lista di stringhe, produrre una stringa contenente le iniziali di ciascuna stringa della lista.

List<String> l = List.of("to", "speak", "the", "truth",
                          "and", "pay", "your", "debts");
// ...
// result = "tsttapyd"

Data una lista di terne di numeri interi, per ciascuna terna verificare se essa costituisce un triangolo. Restituire la lista dei perimetri per le terne che rappresentano triangoli.
In un triangolo, ciascun lato è minore della somma degli altri due.
Si può rappresentare la terna come un array di tre elementi interi

List<List<Integer>> l = List.of(List.of(3, 4, 5), List.of(3, 4, 6),
                                List.of(3, 4, 7), List.of(3, 4, 8));
// ...
// result = [12, 13, 14]

Data una lista di numeri interi positivi, verificare se la lista è ordinata.
Suggerimenti:

  • Si generano gli indici da 0 a n-1
  • Per ciascun valore dell'indice i, si confrontano l'elemento con indice i ed il successivo, se il secondo è minore del primo la lista non è ordinata e si può fermare la verifica

Data una lista di prodotti, restituire il costo totale dei prodotti che hanno un prezzo maggiore di 10.

public record Prodotto(String nome, double prezzo) {}

List<Prodotto> l = List.of(new Prodotto("p1", 5.0), new Prodotto("p2", 10.0),
                            new Prodotto("p3", 15.0), new Prodotto("p4", 20.0));
// ...
// result = 35.0

Produrre una lista contenente i primi n multipli di 7.

int n = 10;
// ...
// result = [0, 7, 14, 21, 28, 35, 42, 49, 56, 63]

Data una lista di utenti, restituire tutti i loro commenti ordinati per data.

public record Utente(String nome, List<Commento> commenti) {}
public record Commento(String testo, Date data) {}

List<Utente> l = List.of(
        new Utente("u1",
                List.of(new Commento("c2", LocalDate.of(2021, 1, 2)),
                        new Commento("c1", LocalDate.of(2021, 1, 1)),
                        new Commento("c5", LocalDate.of(2021, 1, 5)))),
        new Utente("u2",
                List.of(new Commento("c4", LocalDate.of(2021, 1, 4)),
                        new Commento("c3", LocalDate.of(2021, 1, 3)))));
// result = ["c1", "c2", "c3", "c4", "c5"]

Restituire gli utenti che hanno pubblicato almeno un commento prima di una certa data

LocalDate date = LocalDate.of(2021, 1, 3);
List<Utente> l = List.of(
        new Utente("u1",
                List.of(new Commento("c2", LocalDate.of(2021, 1, 2)),
                        new Commento("c1", LocalDate.of(2021, 1, 1)),
                        new Commento("c5", LocalDate.of(2021, 1, 5)))),
        new Utente("u2",
                List.of(new Commento("c4", LocalDate.of(2021, 1, 4)),
                        new Commento("c3", LocalDate.of(2021, 1, 3)))));
// result = ["u1"]
}

Restituire l'utente che ha pubblicato il commento più recente

List<Utente> l = List.of(
        new Utente("u1",
                List.of(new Commento("c2", LocalDate.of(2021, 1, 2)),
                        new Commento("c1", LocalDate.of(2021, 1, 1)),
                        new Commento("c5", LocalDate.of(2021, 1, 5)))),
        new Utente("u2",
                List.of(new Commento("c4", LocalDate.of(2021, 1, 4)),
                        new Commento("c3", LocalDate.of(2021, 1, 3)))));
// result = "u1"

Restituire il prodotto più economico

List<Prodotto> l = List.of(new Prodotto("p1", 80), new Prodotto("p2", 50),
                           new Prodotto("p3", 10), new Prodotto("p4", 20));

// result = "p3"
}

Restituire tutte le figure che siano rettangoli o quadrati (tutti gli angoli uguali)

public record Figura(int l1, int l2, int l3, int l4,
                     int a1, int a2, int a3, int a4) {}

List<Figura> l = List.of(new Figura(12, 12, 12, 12, 45, 45, 135, 135),
                         new Figura(2, 2, 2, 2, 90, 90, 90, 90),
                         new Figura(1, 2, 1, 2, 90, 90, 90, 90));
// ...
// result = [Figura2, Figura3]

Restituire uno stream che contenga il lato minore per ogni figura

List<Figura> l = List.of(new Figura(12, 12, 12, 12, 45, 45, 135, 135),
                         new Figura(2, 2, 2, 2, 90, 90, 90, 90),
                         new Figura(1, 2, 1, 2, 90, 90, 90, 90));
// ...
// result = [12, 2, 1]

Restituire il perimetro minore tra tutte le figure

List<Figura> l = List.of(new Figura(12, 12, 12, 12, 45, 45, 135, 135),
                         new Figura(2, 2, 2, 2, 90, 90, 90, 90),
                         new Figura(1, 2, 1, 2, 90, 90, 90, 90));
// ...
// result = 6

Ottenere la somma del valore dell'area di tutte le figure

List<Figura> l = List.of(new Figura(12, 12, 12, 12, 45, 45, 135, 135),
                         new Figura(2, 2, 2, 2, 90, 90, 90, 90),
                         new Figura(1, 2, 1, 2, 90, 90, 90, 90));
// ...
// result = 150

Restituire la somma totale dei costi dei 2 prodotti meno cari

List<Prodotto> l = List.of(new Prodotto("p1", 80),
                           new Prodotto("p2", 40),
                           new Prodotto("p3", 10),
                           new Prodotto("p4", 90));
// ...
// result = 50

Restituire la lista di lati maggiori dei triangoli.

List<Triangolo> l = List.of(new Triangolo(3, 4, 5, 30, 60, 90),
                            new Triangolo(4, 5, 4, 30, 30, 120),
                            new Triangolo(13, 5, 12, 30, 60, 90),
                            new Triangolo(17, 15, 8, 30, 60, 90));
// ...
// result = [5, 5, 13, 17]

Restituire una lista di triangoli isosceli. Un triangolo è isoscele se due suoi lati sono uguali.

List<Triangolo> l = List.of(new Triangolo(3, 4, 5, 30, 60, 90),
                            new Triangolo(4, 5, 4, 30, 30, 120),
                            new Triangolo(13, 5, 12, 30, 60, 90),
                            new Triangolo(17, 15, 8, 30, 60, 90));
// ...
// result = [Triangolo2]

Creare un metodo che prende in ingresso due parametri, min e max, e restituisce una lista di istanze di persona costituita da elementi di gente che hanno età compresa fra min e max.

public record Persona(String nome, int eta, String nazione) {}

int min = 20, max = 40;
List<Persona> l = List.of(new Persona("p1", 10, "n1"),
                          new Persona("p2", 20, "n1"),
                          new Persona("p3", 30, "n2"),
                          new Persona("p4", 40, "n3"),
                          new Persona("p5", 50, "n3"));
// ...
// result = [p2, p3, p4]

Calcolare la somma delle età di tutte le persone nella lista.

public record Persona(String nome, int eta, String nazione) {}

int min = 20, max = 40;
List<Persona> l = List.of(new Persona("p1", 10, "n1"),
                          new Persona("p2", 20, "n1"),
                          new Persona("p3", 30, "n2"),
                          new Persona("p4", 40, "n3"),
                          new Persona("p5", 50, "n3"));
// ...
// result = 140

Restituire la lista di nazioni senza ripetizioni presenti in una lista di gente.

public record Persona(String nome, int eta, String nazione) {}

int min = 20, max = 40;
List<Persona> l = List.of(new Persona("p1", 10, "n1"),
                          new Persona("p2", 20, "n1"),
                          new Persona("p3", 30, "n2"),
                          new Persona("p4", 40, "n3"),
                          new Persona("p5", 50, "n3"));
// ...
// result = [n1, n2, n3]

Restituire una mappa contenente le coppie (nome persone - nazione).

public record Persona(String nome, int eta, String nazione) {}

int min = 20, max = 40;
List<Persona> l = List.of(new Persona("p1", 10, "n1"),
                          new Persona("p2", 20, "n1"),
                          new Persona("p3", 30, "n2"),
                          new Persona("p4", 40, "n3"),
                          new Persona("p5", 50, "n3"));
// ...
// result = {"p1" -> "n1", "p2" -> "n1", "p3" -> "n2", "p4" -> "n3", "p5" -> "n3"}