Adapter, Decorator e Facade Pattern
I pattern strutturali si occupano della composizione di classi e oggetti. Adapter rende compatibili interfacce incompatibili, Decorator aggiunge responsabilità dinamicamente, e Facade fornisce un'interfaccia semplificata a un sistema complesso.
🎯 Cosa Imparerai
- Adapter pattern: compatibilità tra interfacce diverse
- Decorator pattern: estendere funzionalità dinamicamente
- Facade pattern: semplificare sistemi complessi
- Quando usare ciascun pattern strutturale
Adapter Pattern
L'Adapter pattern (o Wrapper) converte l'interfaccia di una classe in un'altra interfaccia che i client si aspettano. Permette a classi con interfacce incompatibili di collaborare.
// Target: interfaccia che il client si aspetta
interface MediaPlayer {{ '{' }}
play(fileName: string): void;
{{ '}' }}
// Adaptee: classe esistente con interfaccia incompatibile
class Mp3Player {{ '{' }}
playMp3(fileName: string): void {{ '{' }}
console.log(`🎵 Playing MP3: ${{ '{' }}fileName{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class Mp4Player {{ '{' }}
playMp4(fileName: string): void {{ '{' }}
console.log(`🎬 Playing MP4: ${{ '{' }}fileName{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class VlcPlayer {{ '{' }}
playVlc(fileName: string): void {{ '{' }}
console.log(`📺 Playing VLC: ${{ '{' }}fileName{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Adapter: adatta l'interfaccia
class Mp4Adapter implements MediaPlayer {{ '{' }}
private mp4Player: Mp4Player;
constructor() {{ '{' }}
this.mp4Player = new Mp4Player();
{{ '}' }}
play(fileName: string): void {{ '{' }}
// Adatta la chiamata
this.mp4Player.playMp4(fileName);
{{ '}' }}
{{ '}' }}
class VlcAdapter implements MediaPlayer {{ '{' }}
private vlcPlayer: VlcPlayer;
constructor() {{ '{' }}
this.vlcPlayer = new VlcPlayer();
{{ '}' }}
play(fileName: string): void {{ '{' }}
this.vlcPlayer.playVlc(fileName);
{{ '}' }}
{{ '}' }}
// Client: usa l'interfaccia target
class AudioPlayer implements MediaPlayer {{ '{' }}
private mp3Player: Mp3Player = new Mp3Player();
play(fileName: string): void {{ '{' }}
const extension = fileName.split('.').pop()?.toLowerCase();
if (extension === 'mp3') {{ '{' }}
this.mp3Player.playMp3(fileName);
{{ '}' }} else if (extension === 'mp4') {{ '{' }}
const adapter = new Mp4Adapter();
adapter.play(fileName);
{{ '}' }} else if (extension === 'vlc') {{ '{' }}
const adapter = new VlcAdapter();
adapter.play(fileName);
{{ '}' }} else {{ '{' }}
console.log(`❌ Format not supported: ${{ '{' }}extension{{ '}' }}`);
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Utilizzo
const player = new AudioPlayer();
player.play("song.mp3"); // 🎵 Playing MP3: song.mp3
player.play("video.mp4"); // 🎬 Playing MP4: video.mp4
player.play("movie.vlc"); // 📺 Playing VLC: movie.vlc
player.play("file.avi"); // ❌ Format not supported: avi
Adapter per API Esterne
Uso pratico: adattare API di terze parti alla tua interfaccia:
// Interfaccia interna del tuo sistema
interface PaymentProcessor {{ '{' }}
processPayment(amount: number, currency: string): Promise<{{ '{' }} success: boolean; transactionId: string {{ '}' }}>;
{{ '}' }}
// API esterna Stripe (simulata)
class StripeAPI {{ '{' }}
async charge(cents: number, currencyCode: string): Promise<{{ '{' }} id: string; status: string {{ '}' }}> {{ '{' }}
console.log(`💳 Stripe: Charging ${{ '{' }}cents / 100{{ '}' }} ${{ '{' }}currencyCode{{ '}' }}`);
return {{ '{' }} id: 'stripe_' + Math.random(), status: 'succeeded' {{ '}' }};
{{ '}' }}
{{ '}' }}
// API esterna PayPal (simulata)
class PayPalAPI {{ '{' }}
async createPayment(amount: number, currency: string): Promise<{{ '{' }} paymentId: string; state: string {{ '}' }}> {{ '{' }}
console.log(`🅿️ PayPal: Creating payment ${{ '{' }}amount{{ '}' }} ${{ '{' }}currency{{ '}' }}`);
return {{ '{' }} paymentId: 'paypal_' + Math.random(), state: 'approved' {{ '}' }};
{{ '}' }}
{{ '}' }}
// Adapter per Stripe
class StripeAdapter implements PaymentProcessor {{ '{' }}
private stripe: StripeAPI;
constructor() {{ '{' }}
this.stripe = new StripeAPI();
{{ '}' }}
async processPayment(amount: number, currency: string): Promise<{{ '{' }} success: boolean; transactionId: string {{ '}' }}> {{ '{' }}
// Stripe usa centesimi invece di dollari
const cents = Math.round(amount * 100);
const result = await this.stripe.charge(cents, currency.toUpperCase());
return {{ '{' }}
success: result.status === 'succeeded',
transactionId: result.id
{{ '}' }};
{{ '}' }}
{{ '}' }}
// Adapter per PayPal
class PayPalAdapter implements PaymentProcessor {{ '{' }}
private paypal: PayPalAPI;
constructor() {{ '{' }}
this.paypal = new PayPalAPI();
{{ '}' }}
async processPayment(amount: number, currency: string): Promise<{{ '{' }} success: boolean; transactionId: string {{ '}' }}> {{ '{' }}
const result = await this.paypal.createPayment(amount, currency.toLowerCase());
return {{ '{' }}
success: result.state === 'approved',
transactionId: result.paymentId
{{ '}' }};
{{ '}' }}
{{ '}' }}
// Client: usa l'interfaccia unificata
class CheckoutService {{ '{' }}
constructor(private processor: PaymentProcessor) {{ '{' }}{{ '}' }}
async checkout(amount: number): Promise<void> {{ '{' }}
console.log(`🛒 Processing checkout: $${{ '{' }}amount{{ '}' }}`);
const result = await this.processor.processPayment(amount, 'USD');
if (result.success) {{ '{' }}
console.log(`✅ Payment successful! Transaction: ${{ '{' }}result.transactionId{{ '}' }}`);
{{ '}' }} else {{ '{' }}
console.log(`❌ Payment failed`);
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Utilizzo: switch tra provider senza modificare CheckoutService
const stripeCheckout = new CheckoutService(new StripeAdapter());
await stripeCheckout.checkout(99.99);
// 💳 Stripe: Charging 9999 USD
// ✅ Payment successful! Transaction: stripe_0.123...
const paypalCheckout = new CheckoutService(new PayPalAdapter());
await paypalCheckout.checkout(49.99);
// 🅿️ PayPal: Creating payment 49.99 usd
// ✅ Payment successful! Transaction: paypal_0.456...
Decorator Pattern
Il Decorator pattern attacca responsabilità aggiuntive a un oggetto dinamicamente. I Decorator forniscono un'alternativa flessibile all'ereditarietà per estendere funzionalità.
// Component: interfaccia base
interface Coffee {{ '{' }}
cost(): number;
description(): string;
{{ '}' }}
// Concrete Component
class SimpleCoffee implements Coffee {{ '{' }}
cost(): number {{ '{' }}
return 2;
{{ '}' }}
description(): string {{ '{' }}
return "Simple coffee";
{{ '}' }}
{{ '}' }}
// Decorator astratto
abstract class CoffeeDecorator implements Coffee {{ '{' }}
constructor(protected coffee: Coffee) {{ '{' }}{{ '}' }}
abstract cost(): number;
abstract description(): string;
{{ '}' }}
// Concrete Decorators
class MilkDecorator extends CoffeeDecorator {{ '{' }}
cost(): number {{ '{' }}
return this.coffee.cost() + 0.5;
{{ '}' }}
description(): string {{ '{' }}
return this.coffee.description() + ", milk";
{{ '}' }}
{{ '}' }}
class SugarDecorator extends CoffeeDecorator {{ '{' }}
cost(): number {{ '{' }}
return this.coffee.cost() + 0.2;
{{ '}' }}
description(): string {{ '{' }}
return this.coffee.description() + ", sugar";
{{ '}' }}
{{ '}' }}
class WhipDecorator extends CoffeeDecorator {{ '{' }}
cost(): number {{ '{' }}
return this.coffee.cost() + 0.7;
{{ '}' }}
description(): string {{ '{' }}
return this.coffee.description() + ", whipped cream";
{{ '}' }}
{{ '}' }}
// Utilizzo: composizione dinamica
let coffee: Coffee = new SimpleCoffee();
console.log(`${{ '{' }}coffee.description(){{ '}' }} - $${{ '{' }}coffee.cost(){{ '}' }}`);
// "Simple coffee - $2"
// Aggiungi latte
coffee = new MilkDecorator(coffee);
console.log(`${{ '{' }}coffee.description(){{ '}' }} - $${{ '{' }}coffee.cost(){{ '}' }}`);
// "Simple coffee, milk - $2.5"
// Aggiungi zucchero
coffee = new SugarDecorator(coffee);
console.log(`${{ '{' }}coffee.description(){{ '}' }} - $${{ '{' }}coffee.cost(){{ '}' }}`);
// "Simple coffee, milk, sugar - $2.7"
// Aggiungi panna
coffee = new WhipDecorator(coffee);
console.log(`${{ '{' }}coffee.description(){{ '}' }} - $${{ '{' }}coffee.cost(){{ '}' }}`);
// "Simple coffee, milk, sugar, whipped cream - $3.4"
Decorator per Logging e Caching
Esempio pratico: aggiungere logging e caching a un service:
// Component interface
interface DataService {{ '{' }}
fetchData(id: string): Promise<any>;
{{ '}' }}
// Concrete component
class APIDataService implements DataService {{ '{' }}
async fetchData(id: string): Promise<any> {{ '{' }}
console.log(`🌐 Fetching data from API for ID: ${{ '{' }}id{{ '}' }}`);
// Simula API call
await new Promise(resolve => setTimeout(resolve, 1000));
return {{ '{' }} id, data: `Data for ${{ '{' }}id{{ '}' }}` {{ '}' }};
{{ '}' }}
{{ '}' }}
// Logging Decorator
class LoggingDecorator implements DataService {{ '{' }}
constructor(private service: DataService) {{ '{' }}{{ '}' }}
async fetchData(id: string): Promise<any> {{ '{' }}
console.log(`📝 [LOG] Fetching data for: ${{ '{' }}id{{ '}' }}`);
const startTime = Date.now();
try {{ '{' }}
const result = await this.service.fetchData(id);
const duration = Date.now() - startTime;
console.log(`📝 [LOG] Success in ${{ '{' }}duration{{ '}' }}ms`);
return result;
{{ '}' }} catch (error) {{ '{' }}
console.log(`📝 [LOG] Error: ${{ '{' }}error{{ '}' }}`);
throw error;
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Caching Decorator
class CachingDecorator implements DataService {{ '{' }}
private cache: Map<string, any> = new Map();
constructor(private service: DataService) {{ '{' }}{{ '}' }}
async fetchData(id: string): Promise<any> {{ '{' }}
if (this.cache.has(id)) {{ '{' }}
console.log(`💾 [CACHE] Cache hit for: ${{ '{' }}id{{ '}' }}`);
return this.cache.get(id);
{{ '}' }}
console.log(`💾 [CACHE] Cache miss for: ${{ '{' }}id{{ '}' }}`);
const result = await this.service.fetchData(id);
this.cache.set(id, result);
return result;
{{ '}' }}
{{ '}' }}
// Retry Decorator
class RetryDecorator implements DataService {{ '{' }}
constructor(
private service: DataService,
private maxRetries: number = 3
) {{ '{' }}{{ '}' }}
async fetchData(id: string): Promise<any> {{ '{' }}
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {{ '{' }}
try {{ '{' }}
console.log(`🔄 [RETRY] Attempt ${{ '{' }}attempt{{ '}' }}/${{ '{' }}this.maxRetries{{ '}' }}`);
return await this.service.fetchData(id);
{{ '}' }} catch (error) {{ '{' }}
lastError = error;
if (attempt < this.maxRetries) {{ '{' }}
console.log(`🔄 [RETRY] Failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
{{ '}' }}
{{ '}' }}
{{ '}' }}
throw lastError;
{{ '}' }}
{{ '}' }}
// Utilizzo: componi decorators
let service: DataService = new APIDataService();
// Aggiungi logging
service = new LoggingDecorator(service);
// Aggiungi caching
service = new CachingDecorator(service);
// Aggiungi retry
service = new RetryDecorator(service, 3);
// Prima chiamata: cache miss, API call
await service.fetchData("123");
// 📝 [LOG] Fetching data for: 123
// 💾 [CACHE] Cache miss for: 123
// 🔄 [RETRY] Attempt 1/3
// 🌐 Fetching data from API for ID: 123
// 📝 [LOG] Success in 1002ms
// Seconda chiamata: cache hit, nessuna API call
await service.fetchData("123");
// 📝 [LOG] Fetching data for: 123
// 💾 [CACHE] Cache hit for: 123
// 📝 [LOG] Success in 2ms
Facade Pattern
Il Facade pattern fornisce un'interfaccia unificata e semplificata a un insieme di interfacce in un sottosistema. Rende il sottosistema più facile da usare.
// Sottosistema complesso: Home Theater
class Amplifier {{ '{' }}
on(): void {{ '{' }} console.log("🔊 Amplifier ON"); {{ '}' }}
setVolume(level: number): void {{ '{' }} console.log(`🔊 Volume set to ${{ '{' }}level{{ '}' }}`); {{ '}' }}
off(): void {{ '{' }} console.log("🔊 Amplifier OFF"); {{ '}' }}
{{ '}' }}
class DvdPlayer {{ '{' }}
on(): void {{ '{' }} console.log("📀 DVD Player ON"); {{ '}' }}
play(movie: string): void {{ '{' }} console.log(`📀 Playing: ${{ '{' }}movie{{ '}' }}`); {{ '}' }}
stop(): void {{ '{' }} console.log("📀 Stopped"); {{ '}' }}
off(): void {{ '{' }} console.log("📀 DVD Player OFF"); {{ '}' }}
{{ '}' }}
class Projector {{ '{' }}
on(): void {{ '{' }} console.log("📽️ Projector ON"); {{ '}' }}
setInput(source: string): void {{ '{' }} console.log(`📽️ Input: ${{ '{' }}source{{ '}' }}`); {{ '}' }}
off(): void {{ '{' }} console.log("📽️ Projector OFF"); {{ '}' }}
{{ '}' }}
class Lights {{ '{' }}
dim(level: number): void {{ '{' }} console.log(`💡 Lights dimmed to ${{ '{' }}level{{ '}' }}%`); {{ '}' }}
on(): void {{ '{' }} console.log("💡 Lights ON"); {{ '}' }}
{{ '}' }}
// ❌ Senza Facade: cliente deve gestire tutto
function watchMovieWithoutFacade(movie: string): void {{ '{' }}
const amp = new Amplifier();
const dvd = new DvdPlayer();
const projector = new Projector();
const lights = new Lights();
lights.dim(10);
projector.on();
projector.setInput("DVD");
amp.on();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
// ... complessità elevata!
{{ '}' }}
// ✅ Con Facade: interfaccia semplificata
class HomeTheaterFacade {{ '{' }}
constructor(
private amp: Amplifier,
private dvd: DvdPlayer,
private projector: Projector,
private lights: Lights
) {{ '{' }}{{ '}' }}
watchMovie(movie: string): void {{ '{' }}
console.log("🎬 Getting ready to watch a movie...\n");
this.lights.dim(10);
this.projector.on();
this.projector.setInput("DVD");
this.amp.on();
this.amp.setVolume(5);
this.dvd.on();
this.dvd.play(movie);
console.log("\n🍿 Enjoy the movie!");
{{ '}' }}
endMovie(): void {{ '{' }}
console.log("\n👋 Shutting down movie theater...\n");
this.dvd.stop();
this.dvd.off();
this.amp.off();
this.projector.off();
this.lights.on();
console.log("\n✅ Movie theater shut down");
{{ '}' }}
{{ '}' }}
// Utilizzo: semplice e chiaro
const homeTheater = new HomeTheaterFacade(
new Amplifier(),
new DvdPlayer(),
new Projector(),
new Lights()
);
homeTheater.watchMovie("Inception");
// 🎬 Getting ready to watch a movie...
// 💡 Lights dimmed to 10%
// 📽️ Projector ON
// 📽️ Input: DVD
// 🔊 Amplifier ON
// 🔊 Volume set to 5
// 📀 DVD Player ON
// 📀 Playing: Inception
// 🍿 Enjoy the movie!
homeTheater.endMovie();
// 👋 Shutting down movie theater...
// 📀 Stopped
// 📀 DVD Player OFF
// 🔊 Amplifier OFF
// 📽️ Projector OFF
// 💡 Lights ON
// ✅ Movie theater shut down
Facade per Database Operations
Esempio pratico: semplificare operazioni database complesse:
// Sottosistema complesso
class ConnectionPool {{ '{' }}
getConnection(): void {{ '{' }} console.log("🔌 Getting connection from pool"); {{ '}' }}
releaseConnection(): void {{ '{' }} console.log("🔌 Releasing connection"); {{ '}' }}
{{ '}' }}
class QueryBuilder {{ '{' }}
buildSelectQuery(table: string, conditions: any): string {{ '{' }}
console.log(`🔨 Building SELECT query for ${{ '{' }}table{{ '}' }}`);
return `SELECT * FROM ${{ '{' }}table{{ '}' }} WHERE ...`;
{{ '}' }}
buildInsertQuery(table: string, data: any): string {{ '{' }}
console.log(`🔨 Building INSERT query for ${{ '{' }}table{{ '}' }}`);
return `INSERT INTO ${{ '{' }}table{{ '}' }} ...`;
{{ '}' }}
{{ '}' }}
class TransactionManager {{ '{' }}
begin(): void {{ '{' }} console.log("⚙️ Transaction started"); {{ '}' }}
commit(): void {{ '{' }} console.log("✅ Transaction committed"); {{ '}' }}
rollback(): void {{ '{' }} console.log("↩️ Transaction rolled back"); {{ '}' }}
{{ '}' }}
class CacheManager {{ '{' }}
get(key: string): any {{ '{' }}
console.log(`💾 Cache lookup: ${{ '{' }}key{{ '}' }}`);
return null;
{{ '}' }}
set(key: string, value: any): void {{ '{' }}
console.log(`💾 Cache set: ${{ '{' }}key{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Facade: semplifica tutto
class DatabaseFacade {{ '{' }}
private pool = new ConnectionPool();
private queryBuilder = new QueryBuilder();
private txManager = new TransactionManager();
private cache = new CacheManager();
async findUser(id: string): Promise<any> {{ '{' }}
// Controlla cache
const cached = this.cache.get(`user:${{ '{' }}id{{ '}' }}`);
if (cached) return cached;
// Esegui query
this.pool.getConnection();
const query = this.queryBuilder.buildSelectQuery('users', {{ '{' }} id {{ '}' }});
console.log(`🔍 Executing: ${{ '{' }}query{{ '}' }}`);
const user = {{ '{' }} id, name: 'Alice' {{ '}' }}; // Simula risultato
this.cache.set(`user:${{ '{' }}id{{ '}' }}`, user);
this.pool.releaseConnection();
return user;
{{ '}' }}
async createUser(userData: any): Promise<void> {{ '{' }}
this.pool.getConnection();
this.txManager.begin();
try {{ '{' }}
const query = this.queryBuilder.buildInsertQuery('users', userData);
console.log(`➕ Executing: ${{ '{' }}query{{ '}' }}`);
this.txManager.commit();
this.cache.set(`user:${{ '{' }}userData.id{{ '}' }}`, userData);
{{ '}' }} catch (error) {{ '{' }}
this.txManager.rollback();
throw error;
{{ '}' }} finally {{ '{' }}
this.pool.releaseConnection();
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Utilizzo: semplice e pulito
const db = new DatabaseFacade();
await db.findUser("123");
// 💾 Cache lookup: user:123
// 🔌 Getting connection from pool
// 🔨 Building SELECT query for users
// 🔍 Executing: SELECT * FROM users WHERE ...
// 💾 Cache set: user:123
// 🔌 Releasing connection
await db.createUser({{ '{' }} id: "456", name: "Bob" {{ '}' }});
// 🔌 Getting connection from pool
// ⚙️ Transaction started
// 🔨 Building INSERT query for users
// ➕ Executing: INSERT INTO users ...
// ✅ Transaction committed
// 💾 Cache set: user:456
// 🔌 Releasing connection
Quando Usare Ogni Pattern
✅ Usa Adapter quando:
- Vuoi usare classi esistenti ma le loro interfacce sono incompatibili
- Integrare librerie di terze parti (API payment, logging, ecc.)
- Migrare da un sistema vecchio a uno nuovo gradualmente
- Uniform interface per componenti eterogenei
✅ Usa Decorator quando:
- Vuoi aggiungere responsabilità dinamicamente senza ereditarietà
- L'ereditarietà non è pratica (troppe combinazioni)
- Cross-cutting concerns: logging, caching, validation
- Composizione flessibile di funzionalità
✅ Usa Facade quando:
- Vuoi semplificare un sistema complesso
- Ridurre dipendenze tra client e sottosistema
- Definire entry point per layer (database, API)
- Nascondere complessità interna
Tutti e Tre i Pattern Combinati
// ADAPTER: Adatta API esterne
interface ShippingService {{ '{' }}
calculateShipping(weight: number): number;
{{ '}' }}
class FedExAPI {{ '{' }}
computeCost(kg: number): number {{ '{' }} return kg * 5; {{ '}' }}
{{ '}' }}
class FedExAdapter implements ShippingService {{ '{' }}
private fedex = new FedExAPI();
calculateShipping(weight: number): number {{ '{' }}
return this.fedex.computeCost(weight);
{{ '}' }}
{{ '}' }}
// DECORATOR: Aggiungi funzionalità
class DiscountDecorator implements ShippingService {{ '{' }}
constructor(
private service: ShippingService,
private discount: number
) {{ '{' }}{{ '}' }}
calculateShipping(weight: number): number {{ '{' }}
const cost = this.service.calculateShipping(weight);
return cost * (1 - this.discount);
{{ '}' }}
{{ '}' }}
class InsuranceDecorator implements ShippingService {{ '{' }}
constructor(private service: ShippingService) {{ '{' }}{{ '}' }}
calculateShipping(weight: number): number {{ '{' }}
const cost = this.service.calculateShipping(weight);
return cost + 2.99; // Assicurazione fissa
{{ '}' }}
{{ '}' }}
// FACADE: Semplifica checkout
class PaymentGateway {{ '{' }}
charge(amount: number): void {{ '{' }} console.log(`💳 Charged $${{ '{' }}amount{{ '}' }}`); {{ '}' }}
{{ '}' }}
class InventorySystem {{ '{' }}
reserve(productId: string): void {{ '{' }} console.log(`📦 Reserved product ${{ '{' }}productId{{ '}' }}`); {{ '}' }}
{{ '}' }}
class EmailService {{ '{' }}
sendConfirmation(email: string): void {{ '{' }} console.log(`📧 Confirmation sent to ${{ '{' }}email{{ '}' }}`); {{ '}' }}
{{ '}' }}
class CheckoutFacade {{ '{' }}
constructor(
private shippingService: ShippingService,
private payment: PaymentGateway,
private inventory: InventorySystem,
private email: EmailService
) {{ '{' }}{{ '}' }}
processOrder(productId: string, weight: number, userEmail: string): void {{ '{' }}
console.log("🛒 Processing order...\n");
// Calcola spedizione (con adapter + decorators)
const shippingCost = this.shippingService.calculateShipping(weight);
console.log(`📮 Shipping: $${{ '{' }}shippingCost.toFixed(2){{ '}' }}`);
// Processa pagamento
const total = 50 + shippingCost; // 50 = prezzo prodotto
this.payment.charge(total);
// Riserva inventario
this.inventory.reserve(productId);
// Invia conferma
this.email.sendConfirmation(userEmail);
console.log("\n✅ Order completed!");
{{ '}' }}
{{ '}' }}
// Utilizzo combinato
let shipping: ShippingService = new FedExAdapter();
shipping = new DiscountDecorator(shipping, 0.1); // 10% sconto
shipping = new InsuranceDecorator(shipping); // Assicurazione
const checkout = new CheckoutFacade(
shipping,
new PaymentGateway(),
new InventorySystem(),
new EmailService()
);
checkout.processOrder("PROD123", 2.5, "user@example.com");
// 🛒 Processing order...
// 📮 Shipping: $14.24 (2.5kg * 5 * 0.9 + 2.99)
// 💳 Charged $64.24
// 📦 Reserved product PROD123
// 📧 Confirmation sent to user@example.com
// ✅ Order completed!
Conclusione
Questi tre pattern strutturali risolvono problemi complementari. Adapter integra sistemi incompatibili, Decorator estende funzionalità dinamicamente senza ereditarietà, e Facade nasconde complessità fornendo interfacce semplici. Usali insieme per architetture flessibili e manutenibili.
🎯 Punti Chiave
- Adapter = compatibilità tra interfacce incompatibili
- Decorator = aggiungi funzionalità dinamicamente (composizione > ereditarietà)
- Facade = semplifica sistemi complessi con interfaccia unificata
- Adapter per integrare API esterne
- Decorator per cross-cutting concerns (logging, caching)
- Facade per nascondere complessità e ridurre accoppiamento
Domande frequenti
Qual è il principale scopo dell'Adapter Pattern?
L'Adapter pattern converte l'interfaccia di una classe in un'altra interfaccia che i client si aspettano, permettendo a classi con interfacce incompatibili di collaborare.
Qual è la principale differenza tra Decorator e ereditarietà?
Il Decorator pattern attacca responsabilità aggiuntive a un oggetto dinamicamente, fornendo un'alternativa flessibile all'ereditarietà per estendere funzionalità.
Qual è il vantaggio principale del Facade Pattern?
Il Facade pattern fornisce un'interfaccia unificata e semplificata a un insieme di interfacce in un sottosistema, rendendo il sottosistema più facile da usare.
Perché è importante combinare i tre pattern strutturali?
I tre pattern strutturali risolvono problemi complementari e possono essere utilizzati insieme per creare architetture flessibili e manutenibili.

Commenti
Caricamento commenti...
Accedi per commentare