Java attributi
Final, Static e Interface
Final: Fa diventare l'attributo immutabile. Per i metodi, blocca l'ereditarietà (non possono essere ereditati).
Static: Fa diventare l'attributo condiviso tra le varie istanze della classe.
Interface: Sono metodi astratti, non posso istanziarla.
Binding classi e gerarchie
Classi statiche e dinamiche
Padre x = new Figlio(); OK!
Figlio x = new Padre(); NO!
Overriding e overloading
Overriding: Sovrascrive una funzione.
Overloading: Cambio tipi parametri (parametro = classe Figlio).
Binding dinamico
Nel binding dinamico vengono considerati i tipi dinamici di chiamante e parametri.
Estendibilità e principio di Liskov
L'estendibilità tramite ereditarietà
È garantita solo se possiamo sostituire oggetti della sottoclasse a quelli della classe padre.
Principio di sostituzione di Liskov
Gli oggetti della sottoclasse devono rispettare il contratto della sovraclasse, ma il contratto può essere esteso per coprire ulteriori casi.
Regole
- Regola della segnatura: Un sottotipo deve avere tutti i metodi del sovratipo e segnature compatibili.
- Regola dei metodi: Le chiamate ai metodi del sottotipo devono comportarsi in modo compatibile a quelle del sovratipo.
- Regola delle proprietà: Il sottotipo deve preservare tutti i public invarianti.
Testing
Coverage
- Statements: Tutte le istruzioni.
- Edge Coverage: Tutti i rami.
- Path Coverage: Tutti i rami con ogni combinazione logica e tutte le possibili combinazioni di percorsi.
Sono tagliate le condizioni: (False || Ex), (True && Faux) → Ex ritorna ignorato.
Anche le condizioni d'uscita dei cicli sono decisioni.
Collezioni in Java
Dichiarazione e metodi
List <Tipo> Nome = new ArrayList<Tipo>();
| Metodo | Descrizione |
|---|---|
| public boolean add(Object element) | Inserisce un elemento nella collezione. |
| Object get(int index) | Ritorna l'elemento alla posizione specificata nella collezione. |
| public boolean addAll(Collection c) | Inserisce gli elementi della collezione specificata nella collezione invocante. |
| public boolean remove(Object element) | Elimina un elemento dalla collezione. |
| public int size() | Ritorna il numero totale di elementi nella collezione. |
| public void clear() | Rimuove tutti gli elementi dalla collezione. |
| public boolean contains(Object element) | Cerca un elemento nella collezione. |
| public boolean containsAll(Collection c) | Cerca la collezione specificata nella collezione. |
| public Iterator iterator() | Ritorna un iteratore. |
| public boolean isEmpty() | Controlla se la collezione è vuota. |
| public boolean equals(Object element) | Confronta due collezioni. |
Metodi di ArrayList
- indexOf(value): Ritorna il primo indice dove il valore dato è trovato nella lista (-1 se non trovato).
- toString(): Ritorna una rappresentazione in stringa della lista, ad esempio "[3, 42, -7, 15]".
Programmazione funzionale
Sintassi e metodi
Obj. method(Type Par1, Type Par2) → { funzione }
Posso trasformare la collection in uno stream con collection.stream()
Funzioni native
- forEach(x → fcn) → Esegue fcn per ogni valore dello stream
- filter(x → condi) → Filtra lasciando nello stream solo gli elementi che rendono vera x
- map(x → x.*.2etc) → Trasforma i dati dello stream
- distinct() → Elimina i duplicati
- sorted() → Ordina
- reduce(x → acc+o, fcmb(a,b)) → Unisce gli stream risultanti
- collect(collection.total(x)) → Trasforma lo stream in una collection
Operazioni meno comuni
| Operazione | Method Signature | Tipo di Operazione | Descrizione |
|---|---|---|---|
| filter() | Stream<T> filter(Predicate<T> predicate) | Intermedia | Ritorna uno stream di elementi che soddisfano il predicato dato. |
| map() | Stream<R> map(Function<T, R> mapper) | Intermedia | Ritorna uno stream con risultati dopo aver applicato la funzione data agli elementi dello stream. |
| distinct() | Stream<T> distinct() | Intermedia | Ritorna uno stream di elementi unici. |
| sorted() | Stream<T> sorted() | Intermedia | Ordina gli elementi secondo l'ordine naturale. |
| limit() | Stream<T> limit(long maxSize) | Intermedia | Ritorna uno stream contenente i primi n elementi. |
| skip() | Stream<T> skip(long n) | Intermedia | Ritorna uno stream dopo aver saltato i primi n elementi. |
| forEach() | void forEach(Consumer<T> action) | Terminale | Performa un'azione su tutti gli elementi dello stream. |
| toArray() | Object[] toArray() | Terminale | Ritorna un array contenente gli elementi dello stream. |
| reduce() | T reduce(T identity, BinaryOperator<T> accumulator) | Terminale | Performa un'operazione di riduzione sugli elementi dello stream usando un valore iniziale e un'operazione binaria. |
| collect() | R collect(Collector<T, ?, R> collector) | Terminale | Ritorna un contenitore di risultato mutabile come List o Set. |
| min() | Optional<T> min(Comparator<T> comparator) | Terminale | Ritorna l'elemento minimo in uno stream avvolto in un oggetto Optional. |
| max() | Optional<T> max(Comparator<T> comparator) | Terminale | Ritorna l'elemento massimo in uno stream avvolto in un oggetto Optional. |
| count() | long count() | Terminale | Ritorna il numero di elementi in uno stream. |
| anyMatch() | boolean anyMatch(Predicate<T> predicate) | Terminale | Ritorna true se almeno un elemento dello stream corrisponde al predicato dato. |
| allMatch() | boolean allMatch(Predicate<T> predicate) | Terminale | Ritorna true se tutti gli elementi dello stream corrispondono al predicato dato. |
| noneMatch() | boolean noneMatch(Predicate<T> predicate) | Terminale | Ritorna true solo se tutti gli elementi dello stream non corrispondono al predicato dato. |
| findFirst() | Optional<T> findFirst() | Terminale | Ritorna il primo elemento di uno stream avvolto in un oggetto Optional. |
| findAny() | Optional<T> findAny() | Terminale | Ritorna casualmente un elemento di uno stream. |
Optional
Permette di operare su dati ignorando il fatto che alcuni dati possano essere assenti (NULL).
Creazione
public class Persona {
private String nome;
private Optional<String> indirizzo;
public Persona(String nome, String indirizzo) {
this.nome = nome;
this.indirizzo = Optional.ofNullable(indirizzo);
}
public Persona(String nome) {
this.nome = nome;
this.indirizzo = Optional.empty();
}
public String getNome() {
return nome;
}
public Optional<String> getIndirizzo() {
return indirizzo;
}
}
- Optional.of(val) = crea un optional con quel valore
- Optional.empty() = crea un optional vuoto
Utilizzo
Optional<Persona> o = ...
String commenti = o
.flatMap(p -> p.getIndirizzo())
.flatMap(i -> i.getCommenti())
.orElse("no comment");
- o.get() (sconsigliato)
- o.ifPresent(fun) = esegue una funzione su <T> se dati presenti, la esegue
- o.flatMap(m -> Optional<T>) = esegue una funzione da <T> -> Optional<T>
- o.orElse(val) = restituisce T/val se esiste
Programmazione concorrente
Creazione e gestione di thread
Come far partire un Thread
Timer timer = new Timer(…);
Thread t = new Thread(timer);
t.start();
public class Timer implements Runnable {
@Override
public void run() {
}
}
Main class
Nel main devo dichiarare un oggetto thread e inizializzarlo dandogli come parametro la classe apposita.
La classe deve implementare la classe runnable e fare l'override del metodo run.
Programmazione funzionale
Runnable r = () -> doSomething(); t = new Thread(r); t.start();
— Oppure —
Thread t = new Thread( () -> doSomething() ); t.start();
Viene creata una nuova classe che implementa runnable e implementa il metodo run con il metodo "doSomething".
Wait e notify
public void addAccount(String clientName) {
synchronized(accName) {
accName.add(clientName);
if(accName.contains(clientName))
accName.add(clientName);
accBalance.add(0.0);
}
accName.notifyAll();
}
public double getBalance(String clientName) {
synchronized(accName) {
while(!accName.contains(clientName))
try { accName.wait(); }
catch(InterruptedException ex) { ex.printStackTrace(); }
int pos = accName.indexOf(clientName);
return accBalance.get(pos);
}
}
- Uso synchronized per eliminare gli accessi concorrenti.
- Posso mettere in pausa un Thread con wait() (con try-catch), in questo modo viene rilasciato il lock.
- I metodi in wait sono svegliati da notifyAll().
- Posso dichiarare attributi utili: solo come lock = new Object().
In maniera asincrona
Quando l'esercizio chiede di fare qualcosa in "maniera asincrona" rispetto agli altri metodi: dobbiamo creare un nuovo Thread che sia comunque ben "sincronizzato e non causi conflitti" con gli altri Thread.
public void setAllAsync(long val) {
new Thread() {
public void run() {
synchronized(data) {
code;
data.notifyAll();
}
}
}.start();
}
JML
Invariante e astrazione
Formalizzazione di una proprietà: astrazione di una classe.
Osservatore
Sono metodi puri che restituiscono informazioni sullo stato. Posso avere JML solo su metodi attributi pubblici e puri.
Indicatori
- Requires: Pre-condizione
- Ensures: Post-condizione
- Signals (Exc): Eccezioni Bc condizione Eccezionale
- Also: Specifica sotto metodo
- forall: (forall type x; range; condizione)
- Exist: (Exist type x; condizione;)
- Result: \Result indica il valore di ritorno del metodo
- Old: \Old (old) mem lo stato dell'istanza prima del metodo
- Contains: reso x ∈ Struttura
- num_of: (Num_of int i; P(i, a[i]) n° di i che rendono vera Pi0 ≤ a[i]0
I metodi
"Puri" non modificano lo stato.
Ricade di esplicitare che lo stato non cambia quando serve.
Tips
- Devo mettere le condizioni negli ENSURES che escludono i SIGNAL
- I SIGNAL non escludono i REQUIRES
UML
Tipi di diagrammi
- Struttura: Diagrammi di classi e oggetti
- Comportamento: Diagrammi dei casi d'uso
- Interazione: Diagrammi di sequenza
Diagramma di classi
Classi
Attributi → [visibilità] [nome] : [tipo]
Metodi → [visibilità] [nome (lista parametri)] : [tipo restituito]
[visibilità]: + = public, - = private, # = protected, ~ = friendly
Associazioni
Indicano una relazione tra le classi. Nell'implementazione delle classi viene aggiunto un numero di istanze della classe in relazione con quella da implementare.
Aggregazioni
Forma particolare di associazione. Una parte è in relazione con l'oggetto. Tutte le parti esistono "singolarmente".
Composizioni
Aggregazione forte. Le parti componenti non esistono senza il contenitore. In JAVA Aggregazione ≠ Composizione.
Ereditarietà
Padre <|-- Figlio. Viene implementata con "class Figlio extends Padre".
Interfaccia
Non posso istanziare un'interfaccia.
Dipendenze
La classe persona ha bisogno della classe libro per funzionare. Lo indico in questo modo.
Diagramma d'interazione
Descrivono il comportamento dinamico di un gruppo di "oggetti" che interagiscono per risolvere il problema.
Tipi di messaggi
- Sincrono -->
- Asincrono ->
- Risposta - - >(metodi) Termine L.imoTerminizazione oggetto
Pattern
Tipi di pattern
- Creazionali: riguardano il processo di creazione di oggetti.
- Strutturali: riguardano composizione di classi e oggetti.
- Comportamentali: riguardano interazioni tra oggetti e si distribuiscono responsabilità.
Factory Method
Serve per disaccoppiare il codice che fa uso di un tipo da quello che ne implementa il costruttore.
- L'interfaccia che viene chiamata per costruire il prodotto esatto.
- La classe factory, che decide quale costruttore usare.
- I costruttori reali.
Singleton
Viene usato quando si vuole una sola istanza (quindi non si vuole rendere pubblico il costruttore). Un metodo statico dà accesso alla sola istanza.
Adapter
Serve per far interagire 2 diversi sistemi. Viene creata una classe che converte le chiamate ai metodi dell'altra classe.
Proxy
Postpone e estrai la istanziazione di oggetti “pesanti”. Si interpone il proxy tra chiamante e chiamato con la stessa interfaccia dell'oggetto pesante. Il proxy può fare preprocessing o rendere le chiamate più facili.
Observer
Più oggetti si mettono in “ascolto” e vengono informati nel caso il Subject cambia stato.
-
Appunti esame Ingegneria del software
-
Appunti di Ingegneria del software
-
Appunti completi corso Ingegneria del Software
-
Appunti Ingegneria del Software