어댑터, 데코레이터 및 외관 패턴
I 구조적 패턴 클래스와 객체의 구성을 다룹니다. 어댑터 호환되지 않는 인터페이스를 호환되게 만들고, 데코레이터 책임을 동적으로 추가합니다. 정면 인터페이스를 제공합니다 복잡한 시스템으로 단순화되었습니다.
🎯 무엇을 배울 것인가
- 어댑터 패턴: 서로 다른 인터페이스 간의 호환성
- 데코레이터 패턴: 기능을 동적으로 확장
- 외관 패턴: 복잡한 시스템 단순화
- 각 구조 패턴을 사용하는 경우
어댑터 패턴
L'어댑터 패턴 (또는 래퍼)는 클래스의 인터페이스를 다음으로 변환합니다. 클라이언트가 기대하는 또 다른 인터페이스. 인터페이스가 있는 클래스를 허용합니다. 협업에 적합하지 않습니다.
// 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
외부 API용 어댑터
실제 사용: 인터페이스에 타사 API를 적용합니다.
// 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...
데코레이터 패턴
Il 데코레이터 패턴 객체에 추가적인 책임을 부여합니다 동적으로. 데코레이터는 상속에 대한 유연한 대안을 제공합니다. 기능을 확장합니다.
// 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"
로깅 및 캐싱을 위한 데코레이터
실제 예: 서비스에 로깅 및 캐싱 추가:
// 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
외관 패턴
Il 외관 패턴 통합되고 단순화된 인터페이스를 제공합니다. 하위 시스템의 인터페이스 집합입니다. 하위 시스템을 사용하기 쉽게 만듭니다.
// 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
실제 예: 복잡한 데이터베이스 작업 단순화:
// 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
각 패턴을 사용하는 경우
✅ 다음과 같은 경우에 어댑터를 사용하세요:
- 기존 클래스를 사용하고 싶지만 해당 클래스의 인터페이스가 호환되지 않습니다
- 타사 라이브러리 통합(결제 API, 로깅 등)
- 기존 시스템에서 새로운 시스템으로 점진적으로 마이그레이션
- 이기종 구성요소에 대한 통일된 인터페이스
✅ 다음과 같은 경우에 Decorator를 사용하세요:
- 당신이 원하는 책임을 동적으로 추가 상속 없이
- 상속은 실용적이지 않습니다(조합이 너무 많음).
- 교차 관심사: 로깅, 캐싱, 검증
- 유연한 기능 구성
✅ 다음과 같은 경우 Facade를 사용하세요.
- 당신이 원하는 복잡한 시스템을 단순화하다
- 클라이언트와 하위 시스템 간의 종속성을 줄입니다.
- 레이어별 진입점 정의(데이터베이스, API)
- 내부 복잡성 숨기기
세 가지 패턴이 모두 결합됨
// 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!
결론
이 세 가지 구조 패턴은 상호 보완적인 문제를 해결합니다. 어댑터 호환되지 않는 시스템을 통합하고, 데코레이터 기능을 동적으로 확장 상속 없이, e 정면 인터페이스를 제공하여 복잡성을 숨깁니다. 간단하다. 유연하고 유지 관리 가능한 아키텍처를 위해 함께 사용하세요.
🎯 핵심 포인트
- 어댑터 = 호환되지 않는 인터페이스 간의 호환성
- 데코레이터 = 동적으로 기능 추가(구성 > 상속)
- Facade = 통합 인터페이스로 복잡한 시스템을 단순화합니다.
- 외부 API를 통합하기 위한 어댑터
- 크로스커팅 문제를 위한 데코레이터(로깅, 캐싱)
- 복잡성을 숨기고 결합을 줄이는 외관







