Ingegneria del Software - Facade, Adapter, Prototype, State
Descrizione dei design pattern Facade, Adapter, Prototype e State.
Facade
Il facade è un design pattern strutturale.
Definisce un'interfaccia semplificata verso una libreria, un framework o qualsiasi altro sistema complesso.
Problema e soluzione
Compiere una macro operazione richiede molte chiamate a metodi di diverse classi
Le operazioni di basso livello dovrebbero essere nascoste al client che non le deve utilizzare
Il facade fornisce un'interfaccia semplificata che raggruppa internamente le sotto-operazioni
Vengono esposti solo i metodi che interessano al client
UML
Loading diagram...
Diagramma di sequenza
Loading diagram...
Codice
public class VideoConverter { public VideoFile convert(String inputFile, Codec codec) { VideoFile file = new VideoFile(inputFile); Codec ogCodec = file.getCodec(); Converter converter = new Converter(ogCodec, codec); return converter.convert(file); }}
Possibili applicazioni
Creare un'interfaccia di alto livello per un sistema complesso
Nascondere una serie di richieste REST
Nascondere una serie di query ad un database
Pro e contro
Rende più semplice l'utilizzo di un sistema complesso
Se il facade inizia a nascondere troppe sotto-operazioni, viola il principio di responsabilità singola
Adapter
L'adapter è un design pattern strutturale.
Agevola l'interoperabilità tra interfacce diverse.
Problema e soluzione
È necessario cambiare una dipendenza, cercando di minimizzare le modifiche al codice esistente
L'api esterna non è direttamente compatibile con le interfacce del sistema
Nascondere le interfacce indesiderate dietro un oggetto adapter
Sarà compito dell'adapter tradurre le richieste del client in chiamate comprensibili all'api esterna
UML e diagramma di sequenza
Loading diagram...
Loading diagram...
Codice
// JSList<T> è l'interfaccia target, JSListAdapter<T> è l'adapterpublic class JSListAdapter<T> implements JSList<T> { // List è l'adaptee private List<T> list; public JSListAdapter(List<T> list) { this.list = list; } public int length() { return list.size(); } public T at(int index) { return list.get(index); } public void push(T item) { list.add(item); }}
Varianti
Adapter a due vie
Nell'adapter a due vie, l'adapter, oltre l'interfaccia target, implementa anche quella dell'adaptee.
Può essere utilizzato sia come adapter che come adaptee.
Class adapter vs Object adapter
Nel class adapter, l'adapter estende l'adaptee.
Nell'object adapter, l'adapter contiene un'istanza dell'adaptee.
Possibili applicazioni
Tradurre le richieste di un client in chiamate comprensibili ad un'api esterna
Cambiare una libreria esterna senza modificare il codice del client
Semplificare un interfaccia limitandosi a mostrare solo le funzionalità che interessano al client
Pro e contro
Minore dipendenza da una specifica interfaccia
Aggiunta di controlli e validazioni personalizzati sulle richieste
Possibile peggioramento delle prestazioni
Prototype
Il prototype è un design pattern creazionale.
Permette di creare nuovi oggetti clonandone uno esistente.
Problema e soluzione
È necessario creare un oggetto con lo stesso stato di un altro già esistente
Bisogna includere anche campi privati e protetti
È necessario assicurarsi che eventuali riferimenti diventino indipendenti
Delegare all'oggetto stesso la sua clonazione
Assicurarsi che la clonazione sia profonda
UML
Loading diagram...
Codice
public class User implements Cloneable { private String name; private int age; private Address address; @Override public User clone() { User clone = (User) super.clone(); clone.address = this.address.clone(); return clone; }}
In java tutti gli oggetti possiedono un metodo clone().
Per poterlo utilizzare però, bisogna implementare l'interfaccia Cloneable e cambiare la visibilità del metodo a public.
Usando il metodo di default, la copia è shallow.
Possiamo fare un override che faccia il clone in modo ricorsivo.
Possibili applicazioni
Creare due oggetti con lo stesso stato, ma con riferimenti diversi
Assicurarsi che la clonazione sia profonda
Pro e contro
Possibilità di clonare oggetti complessi
Previsto dalla libreria standard
Si potrebbe incappare in dipendenze circolari
State
Lo state è un design pattern comportamentale.
Consente di cambiare radicalmente il comportamento di un oggetto in base allo stato interno, in maniera simile ad un automa a stati finiti.
Problema e soluzione
Il comportamento dell'oggetto dipende dal suo stato interno, definito a runtime
Le operazioni eseguite variano notevolmente in base allo stato interno
Il numero di stati è finito e vi sono delle regole di transizione tra stati
Inserire ogni ramo condizionale in una classe separata
Definire un contesto che utilizzi le classi dello stato per gestire le richieste
Modificare lo stato del contesto in base alle regole di transizione
UML e diagramma di sequenza
Loading diagram...
Loading diagram...
Codice context
class MusicPlayer { private PlayerState state; public MusicPlayer() { state = new PlayerStopState(); } public void play() { state = state.play(); } public void stop() { state = state.stop(); } public void sound() { state.sound(); }}
class PlayerPlayState implements PlayerState { public PlayerState play() { return this; } public PlayerState stop() { return new PlayerStopState(); } public void sound() { System.out.println("BIP"); }}
class PlayerStopState implements PlayerState { public PlayerState play() { return new PlayerPlayState(); } public PlayerState stop() { return this; } public void sound() { }}
Possibili applicazioni
Stato del personaggio in un gioco (normale, stunnato, buffato, ...)
Stato di un circuito (aperto, chiuso, ...)
Fasi di un processo
Macchina a stati finiti
Pro e contro
Il comportamento dell'oggetto in ogni caso è delegato a classi specializzate
Introdurre nuovi stati è trasparente al client e al contesto
Controllo del flusso simile ad una macchina a stati finiti
Complessità eccessiva per controlli condizionali semplici o pochi stati
Challenge
(Facade) Creare un facade per un sistema di pagamento (verifica della carta, pagamento, invio della fattura...)
(Adapter) Riportare un interfaccia di una struttura dati vista in un altro linguaggio in java
(Prototype) Creare un oggetto che cloni un altro oggetto con copia profonda di due livelli
(State) Creare un oggetto che gestisca lo stato di un personaggio in un gioco (normale, stunnato, buffato, ...) modificando le opzioni che il giocatore può scegliere