コンポジットおよびブリッジパターン
複雑さを管理するための 2 つの高度な構造パターン。 複合 取引する 個々のオブジェクトと構成を均一に (ツリー構造)、 Bridge 爆発を避けるために抽象化を実装から分離します。 サブクラスの組み合わせ論。
🎯 何を学ぶか
- 複合パターン: 部分全体のツリー階層
- 単一オブジェクトと複合オブジェクトを均一に扱う
- ブリッジ パターン: 抽象化と実装の分離
- Bridge でサブクラスの爆発を回避する
複合パターン
Il 複合パターン オブジェクトをツリー構造に構成します。 部分全体の階層を表します。クライアントが個々のオブジェクトを処理できるようにします オブジェクトの構成を均一にします。
// Component: interfaccia comune
interface FileSystemItem {{ '{' }}
getName(): string;
getSize(): number;
print(indent: string): void;
{{ '}' }}
// Leaf: elementi senza figli
class File implements FileSystemItem {{ '{' }}
constructor(
private name: string,
private size: number
) {{ '{' }}{{ '}' }}
getName(): string {{ '{' }}
return this.name;
{{ '}' }}
getSize(): number {{ '{' }}
return this.size;
{{ '}' }}
print(indent: string = ""): void {{ '{' }}
console.log(`${{ '{' }}indent{{ '}' }}📄 ${{ '{' }}this.name{{ '}' }} (${{ '{' }}this.size{{ '}' }}KB)`);
{{ '}' }}
{{ '}' }}
// Composite: elementi con figli
class Directory implements FileSystemItem {{ '{' }}
private children: FileSystemItem[] = [];
constructor(private name: string) {{ '{' }}{{ '}' }}
getName(): string {{ '{' }}
return this.name;
{{ '}' }}
add(item: FileSystemItem): void {{ '{' }}
this.children.push(item);
{{ '}' }}
remove(item: FileSystemItem): void {{ '{' }}
const index = this.children.indexOf(item);
if (index !== -1) {{ '{' }}
this.children.splice(index, 1);
{{ '}' }}
{{ '}' }}
getSize(): number {{ '{' }}
// Somma ricorsiva dei figli
return this.children.reduce((total, child) => total + child.getSize(), 0);
{{ '}' }}
print(indent: string = ""): void {{ '{' }}
console.log(`${{ '{' }}indent{{ '}' }}📁 ${{ '{' }}this.name{{ '}' }} (${{ '{' }}this.getSize(){{ '}' }}KB)`);
this.children.forEach(child => child.print(indent + " "));
{{ '}' }}
{{ '}' }}
// Utilizzo: crea struttura ad albero
const root = new Directory("root");
const documents = new Directory("documents");
documents.add(new File("resume.pdf", 120));
documents.add(new File("cover_letter.docx", 45));
const photos = new Directory("photos");
photos.add(new File("vacation.jpg", 2500));
photos.add(new File("family.png", 1800));
const work = new Directory("work");
work.add(new File("project.zip", 15000));
work.add(new File("notes.txt", 5));
documents.add(work); // Directory annidato
root.add(documents);
root.add(photos);
root.add(new File("readme.txt", 2));
// Tratta tutto uniformemente
root.print();
// 📁 root (19472KB)
// 📁 documents (15170KB)
// 📄 resume.pdf (120KB)
// 📄 cover_letter.docx (45KB)
// 📁 work (15005KB)
// 📄 project.zip (15000KB)
// 📄 notes.txt (5KB)
// 📁 photos (4300KB)
// 📄 vacation.jpg (2500KB)
// 📄 family.png (1800KB)
// 📄 readme.txt (2KB)
console.log(`Total size: ${{ '{' }}root.getSize(){{ '}' }}KB`); // 19472KB
オペレーションとの複合
実用的な例: アクションを含むメニュー システム:
// Component interface
interface MenuComponent {{ '{' }}
getName(): string;
execute(): void;
print(indent: string): void;
{{ '}' }}
// Leaf: azioni singole
class MenuItem implements MenuComponent {{ '{' }}
constructor(
private name: string,
private action: () => void
) {{ '{' }}{{ '}' }}
getName(): string {{ '{' }}
return this.name;
{{ '}' }}
execute(): void {{ '{' }}
console.log(`▶️ Executing: ${{ '{' }}this.name{{ '}' }}`);
this.action();
{{ '}' }}
print(indent: string = ""): void {{ '{' }}
console.log(`${{ '{' }}indent{{ '}' }}• ${{ '{' }}this.name{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Composite: sottomenu
class Menu implements MenuComponent {{ '{' }}
private items: MenuComponent[] = [];
constructor(private name: string) {{ '{' }}{{ '}' }}
getName(): string {{ '{' }}
return this.name;
{{ '}' }}
add(item: MenuComponent): void {{ '{' }}
this.items.push(item);
{{ '}' }}
execute(): void {{ '{' }}
console.log(`📂 Opening menu: ${{ '{' }}this.name{{ '}' }}`);
this.items.forEach(item => item.execute());
{{ '}' }}
print(indent: string = ""): void {{ '{' }}
console.log(`${{ '{' }}indent{{ '}' }}📂 ${{ '{' }}this.name{{ '}' }}`);
this.items.forEach(item => item.print(indent + " "));
{{ '}' }}
{{ '}' }}
// Costruisci menu gerarchico
const mainMenu = new Menu("Main Menu");
const fileMenu = new Menu("File");
fileMenu.add(new MenuItem("New", () => console.log("Creating new file...")));
fileMenu.add(new MenuItem("Open", () => console.log("Opening file...")));
fileMenu.add(new MenuItem("Save", () => console.log("Saving file...")));
fileMenu.add(new MenuItem("Exit", () => console.log("Exiting...")));
const editMenu = new Menu("Edit");
editMenu.add(new MenuItem("Cut", () => console.log("Cutting...")));
editMenu.add(new MenuItem("Copy", () => console.log("Copying...")));
editMenu.add(new MenuItem("Paste", () => console.log("Pasting...")));
const formatMenu = new Menu("Format");
formatMenu.add(new MenuItem("Bold", () => console.log("Applying bold...")));
formatMenu.add(new MenuItem("Italic", () => console.log("Applying italic...")));
editMenu.add(formatMenu); // Sottomenu annidato
mainMenu.add(fileMenu);
mainMenu.add(editMenu);
// Visualizza struttura
mainMenu.print();
// 📂 Main Menu
// 📂 File
// • New
// • Open
// • Save
// • Exit
// 📂 Edit
// • Cut
// • Copy
// • Paste
// 📂 Format
// • Bold
// • Italic
// Esegui tutti
fileMenu.execute();
// 📂 Opening menu: File
// ▶️ Executing: New
// Creating new file...
// ▶️ Executing: Open
// Opening file...
// ▶️ Executing: Save
// Saving file...
// ▶️ Executing: Exit
// Exiting...
UIコンポーネントのコンポジット
現実的な例: ネストされた UI コンポーネント (React/Angular など):
// Component interface
interface UIComponent {{ '{' }}
render(): string;
addClass(className: string): void;
{{ '}' }}
// Leaf: elementi base
class Button implements UIComponent {{ '{' }}
private classes: string[] = [];
constructor(private text: string) {{ '{' }}{{ '}' }}
addClass(className: string): void {{ '{' }}
this.classes.push(className);
{{ '}' }}
render(): string {{ '{' }}
const classAttr = this.classes.length > 0 ? ` class="${{ '{' }}this.classes.join(' '){{ '}' }}"` : '';
return `<button${{ '{' }}classAttr{{ '}' }}>${{ '{' }}this.text{{ '}' }}</button>`;
{{ '}' }}
{{ '}' }}
class Input implements UIComponent {{ '{' }}
private classes: string[] = [];
constructor(
private placeholder: string,
private type: string = "text"
) {{ '{' }}{{ '}' }}
addClass(className: string): void {{ '{' }}
this.classes.push(className);
{{ '}' }}
render(): string {{ '{' }}
const classAttr = this.classes.length > 0 ? ` class="${{ '{' }}this.classes.join(' '){{ '}' }}"` : '';
return `<input type="${{ '{' }}this.type{{ '}' }}" placeholder="${{ '{' }}this.placeholder{{ '}' }}"${{ '{' }}classAttr{{ '}' }} />`;
{{ '}' }}
{{ '}' }}
// Composite: container
class Panel implements UIComponent {{ '{' }}
private children: UIComponent[] = [];
private classes: string[] = [];
constructor(private title?: string) {{ '{' }}{{ '}' }}
add(component: UIComponent): void {{ '{' }}
this.children.push(component);
{{ '}' }}
addClass(className: string): void {{ '{' }}
this.classes.push(className);
{{ '}' }}
render(): string {{ '{' }}
const classAttr = this.classes.length > 0 ? ` class="${{ '{' }}this.classes.join(' '){{ '}' }}"` : '';
const titleHtml = this.title ? `<h3>${{ '{' }}this.title{{ '}' }}</h3>` : '';
const childrenHtml = this.children.map(child => child.render()).join('\n ');
return `<div${{ '{' }}classAttr{{ '}' }}>
${{ '{' }}titleHtml{{ '}' }}
${{ '{' }}childrenHtml{{ '}' }}
</div>`;
{{ '}' }}
{{ '}' }}
// Costruisci UI
const form = new Panel("Login Form");
form.addClass("card");
form.addClass("shadow");
const usernameInput = new Input("Enter username");
usernameInput.addClass("form-control");
const passwordInput = new Input("Enter password", "password");
passwordInput.addClass("form-control");
const submitButton = new Button("Login");
submitButton.addClass("btn");
submitButton.addClass("btn-primary");
form.add(usernameInput);
form.add(passwordInput);
form.add(submitButton);
console.log(form.render());
// <div class="card shadow">
// <h3>Login Form</h3>
// <input type="text" placeholder="Enter username" class="form-control" />
// <input type="password" placeholder="Enter password" class="form-control" />
// <button class="btn btn-primary">Login</button>
// </div>
ブリッジパターン
Il ブリッジパターン このように抽象化をその実装から分離します この 2 つは独立して変化する可能性があるということです。の組み合わせ爆発を解決します 2 つの次元の変動がある場合は、サブクラスを作成します。
// Implementation: interfaccia implementazione
interface Renderer {{ '{' }}
renderCircle(radius: number): void;
renderSquare(side: number): void;
{{ '}' }}
// Concrete Implementations
class VectorRenderer implements Renderer {{ '{' }}
renderCircle(radius: number): void {{ '{' }}
console.log(`🎨 Drawing vector circle with radius ${{ '{' }}radius{{ '}' }}`);
{{ '}' }}
renderSquare(side: number): void {{ '{' }}
console.log(`🎨 Drawing vector square with side ${{ '{' }}side{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class RasterRenderer implements Renderer {{ '{' }}
renderCircle(radius: number): void {{ '{' }}
console.log(`🖼️ Drawing raster circle (pixels) with radius ${{ '{' }}radius{{ '}' }}`);
{{ '}' }}
renderSquare(side: number): void {{ '{' }}
console.log(`🖼️ Drawing raster square (pixels) with side ${{ '{' }}side{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Abstraction: forma astratta
abstract class Shape {{ '{' }}
constructor(protected renderer: Renderer) {{ '{' }}{{ '}' }}
abstract draw(): void;
abstract resize(factor: number): void;
{{ '}' }}
// Refined Abstractions
class Circle extends Shape {{ '{' }}
constructor(renderer: Renderer, private radius: number) {{ '{' }}
super(renderer);
{{ '}' }}
draw(): void {{ '{' }}
this.renderer.renderCircle(this.radius);
{{ '}' }}
resize(factor: number): void {{ '{' }}
this.radius *= factor;
{{ '}' }}
{{ '}' }}
class Square extends Shape {{ '{' }}
constructor(renderer: Renderer, private side: number) {{ '{' }}
super(renderer);
{{ '}' }}
draw(): void {{ '{' }}
this.renderer.renderSquare(this.side);
{{ '}' }}
resize(factor: number): void {{ '{' }}
this.side *= factor;
{{ '}' }}
{{ '}' }}
// Utilizzo: combina astrazioni e implementazioni
const vectorCircle = new Circle(new VectorRenderer(), 5);
vectorCircle.draw(); // 🎨 Drawing vector circle with radius 5
const rasterCircle = new Circle(new RasterRenderer(), 5);
rasterCircle.draw(); // 🖼️ Drawing raster circle (pixels) with radius 5
const vectorSquare = new Square(new VectorRenderer(), 10);
vectorSquare.draw(); // 🎨 Drawing vector square with side 10
const rasterSquare = new Square(new RasterRenderer(), 10);
rasterSquare.draw(); // 🖼️ Drawing raster square (pixels) with side 10
// Senza Bridge: avresti bisogno di VectorCircle, RasterCircle, VectorSquare, RasterSquare
// Con 3 forme e 3 renderer = 9 classi!
// Con Bridge: 3 forme + 3 renderer = 6 classi
ブリッジ: サブクラスの爆発を回避する
Bridge によって解決された問題: 組み合わせ爆発を伴わない 2 つの次元の変動:
// ❌ Approccio con ereditarietà: esplosione di classi
class SmallRedCircle {{ '{' }}{{ '}' }}
class SmallBlueCircle {{ '{' }}{{ '}' }}
class SmallGreenCircle {{ '{' }}{{ '}' }}
class MediumRedCircle {{ '{' }}{{ '}' }}
class MediumBlueCircle {{ '{' }}{{ '}' }}
class MediumGreenCircle {{ '{' }}{{ '}' }}
class LargeRedCircle {{ '{' }}{{ '}' }}
class LargeBlueCircle {{ '{' }}{{ '}' }}
class LargeGreenCircle {{ '{' }}{{ '}' }}
// 3 size × 3 colors = 9 classi solo per Circle!
// Aggiungi Square, Triangle → 27 classi!
// Aggiungi un colore → +9 classi!
// Implementation: colori
interface Color {{ '{' }}
fill(): string;
{{ '}' }}
class Red implements Color {{ '{' }}
fill(): string {{ '{' }} return "red"; {{ '}' }}
{{ '}' }}
class Blue implements Color {{ '{' }}
fill(): string {{ '{' }} return "blue"; {{ '}' }}
{{ '}' }}
class Green implements Color {{ '{' }}
fill(): string {{ '{' }} return "green"; {{ '}' }}
{{ '}' }}
// Abstraction: forme con dimensione
abstract class ColoredShape {{ '{' }}
constructor(protected color: Color) {{ '{' }}{{ '}' }}
abstract draw(): void;
{{ '}' }}
class ColoredCircle extends ColoredShape {{ '{' }}
constructor(color: Color, private size: string) {{ '{' }}
super(color);
{{ '}' }}
draw(): void {{ '{' }}
console.log(`🔴 Drawing ${{ '{' }}this.size{{ '}' }} circle with ${{ '{' }}this.color.fill(){{ '}' }} color`);
{{ '}' }}
{{ '}' }}
class ColoredSquare extends ColoredShape {{ '{' }}
constructor(color: Color, private size: string) {{ '{' }}
super(color);
{{ '}' }}
draw(): void {{ '{' }}
console.log(`🟦 Drawing ${{ '{' }}this.size{{ '}' }} square with ${{ '{' }}this.color.fill(){{ '}' }} color`);
{{ '}' }}
{{ '}' }}
// Utilizzo: combina liberamente
new ColoredCircle(new Red(), "small").draw();
// 🔴 Drawing small circle with red color
new ColoredCircle(new Blue(), "large").draw();
// 🔴 Drawing large circle with blue color
new ColoredSquare(new Green(), "medium").draw();
// 🟦 Drawing medium square with green color
// 2 forme + 3 colori = 5 classi (vs 6 con ereditarietà)
// Aggiungi 1 colore → +1 classe (vs +2)
// Scalabile linearmente!
クロスプラットフォーム UI のブリッジ
実践例: さまざまなプラットフォームで動作する UI:
// Implementation: piattaforme
interface Platform {{ '{' }}
showDialog(title: string, message: string): void;
showNotification(message: string): void;
{{ '}' }}
class WindowsPlatform implements Platform {{ '{' }}
showDialog(title: string, message: string): void {{ '{' }}
console.log(`🪟 Windows Dialog: [${{ '{' }}title{{ '}' }}] ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
showNotification(message: string): void {{ '{' }}
console.log(`🪟 Windows Notification: ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class MacOSPlatform implements Platform {{ '{' }}
showDialog(title: string, message: string): void {{ '{' }}
console.log(`🍎 macOS Alert: [${{ '{' }}title{{ '}' }}] ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
showNotification(message: string): void {{ '{' }}
console.log(`🍎 macOS Banner: ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class LinuxPlatform implements Platform {{ '{' }}
showDialog(title: string, message: string): void {{ '{' }}
console.log(`🐧 Linux Dialog: [${{ '{' }}title{{ '}' }}] ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
showNotification(message: string): void {{ '{' }}
console.log(`🐧 Linux Notify: ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Abstraction: applicazione
abstract class Application {{ '{' }}
constructor(protected platform: Platform) {{ '{' }}{{ '}' }}
abstract run(): void;
{{ '}' }}
class MessageApp extends Application {{ '{' }}
run(): void {{ '{' }}
this.platform.showDialog("New Message", "You have 5 new messages");
this.platform.showNotification("Message received!");
{{ '}' }}
{{ '}' }}
class SettingsApp extends Application {{ '{' }}
run(): void {{ '{' }}
this.platform.showDialog("Settings", "Configure your preferences");
{{ '}' }}
{{ '}' }}
// Utilizzo: stessa app su diverse piattaforme
console.log("--- Windows ---");
const windowsMsg = new MessageApp(new WindowsPlatform());
windowsMsg.run();
// 🪟 Windows Dialog: [New Message] You have 5 new messages
// 🪟 Windows Notification: Message received!
console.log("\n--- macOS ---");
const macMsg = new MessageApp(new MacOSPlatform());
macMsg.run();
// 🍎 macOS Alert: [New Message] You have 5 new messages
// 🍎 macOS Banner: Message received!
console.log("\n--- Linux ---");
const linuxSettings = new SettingsApp(new LinuxPlatform());
linuxSettings.run();
// 🐧 Linux Dialog: [Settings] Configure your preferences
各パターンをいつ使用するか
✅ 次の場合にコンポジットを使用します。
- あなたが表現したいのは 部分全体の階層 (木々)
- クライアントは単一のオブジェクトと構成を処理する必要があります 均一に
- 再帰的構造: ファイル システム、メニュー、UI コンポーネント
- 操作は階層を介して伝播する必要があります
✅ 次の場合にブリッジを使用します。
- あなたが持っている 2 つの次元の変動 独立した
- 避けたいのは 組み合わせ爆発 サブクラスの
- 抽象化と実装は独立して変化する必要がある
- クロスプラットフォーム開発、レンダリング エンジン、データベース ドライバー
コンポジットとブリッジの組み合わせ
// BRIDGE: separare document da storage
interface DocumentStorage {{ '{' }}
save(content: string): void;
load(): string;
{{ '}' }}
class LocalFileStorage implements DocumentStorage {{ '{' }}
save(content: string): void {{ '{' }}
console.log(`💾 Saving to local file: ${{ '{' }}content.substring(0, 30){{ '}' }}...`);
{{ '}' }}
load(): string {{ '{' }}
return "Loaded from local file";
{{ '}' }}
{{ '}' }}
class CloudStorage implements DocumentStorage {{ '{' }}
save(content: string): void {{ '{' }}
console.log(`☁️ Uploading to cloud: ${{ '{' }}content.substring(0, 30){{ '}' }}...`);
{{ '}' }}
load(): string {{ '{' }}
return "Loaded from cloud";
{{ '}' }}
{{ '}' }}
// COMPOSITE: struttura documento
interface DocumentElement {{ '{' }}
render(): string;
{{ '}' }}
class Paragraph implements DocumentElement {{ '{' }}
constructor(private text: string) {{ '{' }}{{ '}' }}
render(): string {{ '{' }}
return `<p>${{ '{' }}this.text{{ '}' }}</p>`;
{{ '}' }}
{{ '}' }}
class Heading implements DocumentElement {{ '{' }}
constructor(
private text: string,
private level: number
) {{ '{' }}{{ '}' }}
render(): string {{ '{' }}
return `<h${{ '{' }}this.level{{ '}' }}>${{ '{' }}this.text{{ '}' }}</h${{ '{' }}this.level{{ '}' }}>`;
{{ '}' }}
{{ '}' }}
class Section implements DocumentElement {{ '{' }}
private elements: DocumentElement[] = [];
add(element: DocumentElement): void {{ '{' }}
this.elements.push(element);
{{ '}' }}
render(): string {{ '{' }}
const content = this.elements.map(e => e.render()).join('\n');
return `<section>\n${{ '{' }}content{{ '}' }}\n</section>`;
{{ '}' }}
{{ '}' }}
// Document con Bridge per storage
class Document {{ '{' }}
private root = new Section();
constructor(private storage: DocumentStorage) {{ '{' }}{{ '}' }}
add(element: DocumentElement): void {{ '{' }}
this.root.add(element);
{{ '}' }}
save(): void {{ '{' }}
const content = this.root.render();
this.storage.save(content);
{{ '}' }}
load(): void {{ '{' }}
const content = this.storage.load();
console.log(`📄 Document loaded: ${{ '{' }}content{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Utilizzo combinato
const localDoc = new Document(new LocalFileStorage());
localDoc.add(new Heading("Introduction", 1));
localDoc.add(new Paragraph("This is the introduction paragraph."));
localDoc.add(new Heading("Details", 2));
localDoc.add(new Paragraph("Here are the details."));
localDoc.save();
// 💾 Saving to local file: <section>
// <h1>Introduction</h1>
// <p>Thi...
const cloudDoc = new Document(new CloudStorage());
cloudDoc.add(new Heading("Cloud Document", 1));
cloudDoc.add(new Paragraph("Stored in the cloud."));
cloudDoc.save();
// ☁️ Uploading to cloud: <section>
// <h1>Cloud Document</h1>
// <p>St...
結論
コンポジットとブリッジは複雑な構造問題を解決します。 複合 単一のオブジェクトと再帰的なツリー構造の構成を統合します。 Bridge 爆発を避けるために抽象化を実装から分離する 組み合わせ論。スケーラブルで保守可能なアーキテクチャにこれらを使用します。
🎯 重要なポイント
- コンポジット = ツリー階層、リーフとコンポジットを同等に扱います
- ファイル システム、メニュー、UI ツリーにコンポジットを使用する
- ブリッジ = 抽象化を実装から分離します
- ブリッジは N×M サブクラスを回避します (N+M クラスになります)
- クロスプラットフォーム、マルチレンダラー、マルチデータベースに Bridge を使用する
- どちらも継承よりも合成を優先します







