Adapter

Il problema analizzato nel pattern Polymorphism (e per motivare le scelte fatte) è più specificatamente un esempio del design pattern GoF strutturale Adapter.

Tabella riassuntiva del pattern
...

NomeAdapter
ProblemaCome gestire interfacce incompatibili, o fornire un'interfaccia stabile a componenti simili ma con interfacce diverse?
SoluzioneConvertire l'interfaccia originale di un componente in un'altra interfaccia, attraverso un oggetto adattatore intermedio.

Prima di tutto chiariamo la formulazione del problema:

  • per interfacce incompatibili intendiamo il caso in cui abbiamo una coppia di oggetti software in una relazione client-server, parliamo di interfacce incompatibili quando l'oggetto server offre servizi di interesse per l'oggetto client, ma l'oggetto client vuole fruire di servizi in modalità diversa da quella prevista dall'oggetto server;
  • per componenti simili con interfacce diverse intendiamo quando ci sono più oggetti server che offrono servizi simili; questi oggetti hanno interfacce simili ma diverse tra di loro. Un oggetto client vuol fruire dei servizi offerti da uno tra questi oggetti server.

Per riassumere: il sistema POS deve supportare diversi tipi di servizi esterni prodotti da terzi, compresi i sistemi per la contabilità e per l'inventario, i servizi di autorizzazione ai pagamenti, i servizi per il calcolo delle imposte, ecc... Ciascuno di essi ha una propria API, diversa da quelle degli altri, che non può essere modificata poiché è fornita da terze parti.

Una soluzione consiste nell'aggiungere un livello di Indirection con oggetti che adattano le interfacce esterne (variabili) a un interfaccia compatibile utilizzata all'interno dell'applicazione.

La soluzione è illustrata nella figura sottostante.
Per esempio, l'interfaccia ITaxCalculatorAdapter è un interfaccia che è implementata dalle classi che sfruttano le API di terze parti, in particolare TaxMasterAdapter e GoodAdGoldTaxPro_Adapter.
Un modulo che ad un certo punto contiene un oggetto che fa riferimento a (definito come) ITaxCalculatorAdapter chiamando il metodo getTaxed(Sale), la richiesta di tale chiamate viene ricevuta dall'Adapter che "adatta" la richiesta all'interfaccia del componente istanziato.
Pasted image 20230522103606.png

Esempio con codice
...

public interface CalculationAdapter { // adapter
    void calculation();
}

public class ClassAAdapter implements CalculationAdapter { // classe che implementa API
    @Override
    public void calculation() {
        /* effettua richiesta verso l'interfaccia esterna che magari riceve le richieste 
           attraverso servizi particolari: HTTP, o altri (formato del server) */
    }
}

public class ClassBAdapter implements CalculationAdapter { // classe che implementa API
    @Override
    public void calculation() {
        /* effettua richiesta verso l'interfaccia esterna che magari riceve le richieste 
           attraverso servizi particolari: HTTP, o altri (formato del server) */
    }
}

public class Main {
    public static void main(String[] args) {
        CalculationAdapter adapter1 = new ClassAAdapter(); 
        // classA è definita con il tipo dell'adapter
        CalculationAdapter adapter2 = new ClassBAdapter();
        // classB è definita con il tipo dell'adapter

        adapter1.calculation();
        // quando viene chiamato calculation, la richiesta di chiamata (grazie al 
        // polimorfismo) è fatta all'adapter che chiama il metodo calculation()
        // per la classe istanziata, in questo caso ClassA.
        adapter2.calculation();
    }
}

In generale, un adattatore riceve richieste dai suoi client, per esempio da un oggetto dello strato del dominio. Queste richieste sono nel "formato del client" dell'adattatore; per esempio un messaggio postSale(Sale) di IAccountingAdapater (vedi figura sopra). L'adattatore poi adatta una richiesta ricevuta in una richiesta nel "formato del server". Come si vede anche nell'esempio sopra un client può tranquillamente chiamare il metodo calculation(), poi l'adattatore esegue le meccaniche per adattare la chiamata all'interfaccia esterna (HTTP, XML, JSON, o altre meccaniche più complesse).
Dopo l'invio di una richiesta può seguire l'invio, da parte del server, di una risposta. La risposta arriva nel formato del server, viene gestita dall'adattatore che la "adatta" al formato del client.

Si noti che l'adattatore contiene la parola Adapter.