アダプター、デコレーター、ファサード パターン
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
データベース操作のファサード
実践例: 複雑なデータベース操作を簡素化します。
// 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、ロギングなど)を統合します。
- 古いシステムから新しいシステムに段階的に移行する
- 異種コンポーネント向けの統一インターフェイス
✅ 次の場合にデコレータを使用します。
- あなたが欲しいのは 責任を動的に追加する 継承なし
- 継承は現実的ではありません (組み合わせが多すぎる)
- 横断的な懸念事項: ロギング、キャッシュ、検証
- 柔軟な機能構成
✅ 次の場合にファサードを使用します。
- あなたが欲しいのは 複雑なシステムを単純化する
- クライアントとサブシステム間の依存関係を軽減する
- レイヤーごとにエントリ ポイントを定義 (データベース、API)
- 内部の複雑さを隠す
3 つのパターンをすべて組み合わせたもの
// 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!
結論
これら 3 つの構造パターンは相補的に問題を解決します。 アダプタ 互換性のないシステムを統合する デコレーター 機能を動的に拡張します 継承なし、e ファサード インターフェイスを提供することで複雑さを隠します シンプル。これらを組み合わせて使用すると、柔軟で保守可能なアーキテクチャが実現します。
🎯 重要なポイント
- アダプター = 互換性のないインターフェイス間の互換性
- デコレーター = 機能を動的に追加します (構成 > 継承)
- ファサード = 統一されたインターフェイスにより複雑なシステムを簡素化します
- 外部APIを統合するアダプター
- 横断的な問題のデコレーター (ロギング、キャッシュ)
- 複雑さを隠し、結合を軽減するファサード







