DESIGN PATTERN: ADAPTER

L’Adapter (o Adattatore / Wrapper) è un pattern strutturale che consente a due classi con interfacce incompatibili di collaborare.

L’adattatore funge da “traduttore” tra l’interfaccia attesa dal codice client (Target) e quella realmente offerta da un oggetto esistente (Adaptee), senza modificare né il client né la classe originale.

QUANDO UTILIZZARLO

  • Integrazione di librerie o componenti di terze parti.
  • Uniformare formati di input/output differenti (es. XML ⇆ JSON, sistemi di coordinate diversi).
  • Migrare verso una nuova API senza interrompere il funzionamento del codice esistente.

VANTAGGI

  • Basso accoppiamento: il client dipende solo dall’interfaccia Target.
  • Nessuna modifica a codice esistente: adattamento non invasivo.
  • Flessibilità nella sostituzione: puoi introdurre più adattatori per transizioni graduali.
  • Riutilizzo di codice: componenti vecchi possono integrarsi con sistemi nuovi.

POTENZIALI SVANTAGGI

  • Aumento della complessità: più formati = più adapter da gestire.
  • Prestazioni: un layer aggiuntivo può introdurre overhead.
  • Soluzione temporanea: spesso è un “cerotto” invece di una riprogettazione strutturale.

CODICE

/**
 * Sorgenti dati JSON ok
 */
class JSONData {
	content: Object;

	constructor(content: Object) {
		this.content = content;
	}
}

/**
 * Sorgenti dati XML ok => da adattare con il nostro design pattern
 */
class XMLData {
	content: string;

	constructor(content: string) {
		this.content = content;
	}
}

/**
 * ADAPTER
 * Interfaccia comune per l'adattamento a un MCM
 */
interface CommonData {
	convertToCommonData(): JSONData;
}

/**
 * CONVERTITORE JSON
 */
class convertJSONData implements CommonData {
	jsonData: JSONData;

	constructor(jsonData: JSONData) {
		this.jsonData = jsonData;
	}

	convertToCommonData(): JSONData {
		return this.jsonData;
	}
}

/**
 * CONVERTITORE XML
 */
class convertXMLData implements CommonData {
	xmlData: XMLData;

	constructor(xmlData: XMLData) {
		this.xmlData = xmlData;
	}

	convertToCommonData(): JSONData {
		//Parsing manuale semplificato (solo per esempio)
		const matches = this.xmlData.content.match(/<campo1 xml>(.*?)<\/campo1 xml>[\s\S]*<campo2 xml>(.*?)<\/campo2 xml>/);

		if (!matches) {
			throw new Error("Formato XML non valido");
		}

		const jsonObj: Object = {
			"campo 1 xml": matches[1],
			"campo 2 xml": matches[2]
		};

		return new JSONData(jsonObj);
	}
}

//USO ADAPTER
const jsonData: JSONData = new JSONData({
	"campo 1 json": "valore 1 json",
	"campo 2 json": "valore 2 json",
});

const xmlData: XMLData = new XMLData(`
  <root>
    <campo1 xml>valore 1 xml</campo1 xml>
    <campo2 xml>valore 2 xml</campo2 xml>
  </root>
`);

const commonDataFromJSON: JSONData = new convertJSONData(jsonData).convertToCommonData();
const commonDataFromXML: JSONData = new convertXMLData(xmlData).convertToCommonData();
console.log("JSON convertito a JSON: ", commonDataFromJSON);
console.log("XML convertito a JSON: ", commonDataFromXML);