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'adapter
public 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(); }
}

Codice state

interface PlayerState {
    PlayerState play();
    PlayerState stop();
    void 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