iT邦幫忙

2024 iThome 鐵人賽

DAY 8
1

裝飾者模式可以讓你在不改變原有物件的情況下,動態地為物件新增行為或功能。

生活案例

想像你點了一杯咖啡,這就是一個基本物件。你可以在咖啡上加一些配料,像是鮮奶油或糖漿,但你不需要改變咖啡本身。裝飾者模式就像這些配料,它們可以一層層地加在咖啡上,為咖啡增添風味。但無論你加了什麼,底下的咖啡還是原來那杯,並且隨時可以再加新的配料。

在程式設計中,裝飾者模式能夠讓程式更靈活,因為我們可以在不修改原始碼的情況下為物件加上新功能。這不僅提升了程式的擴展性,還能增加程式的可讀性與維護性。

舉個例子

假設我們有一個 markdown 轉譯器,可以將 markdown 語法轉換成 HTML 字串。我們可以使用裝飾者模式來擴充功能,比如支援自訂語法或是客製化 HTML 字串。這樣,我們只需要加上適當的“裝飾”就能滿足不同需求,而不必重新設計整個轉譯器。

定義轉譯器的抽象類別

abstract class MarkdownParser {
  abstract toHTMLString(markdown: string): string;
}

定義基礎轉譯器

class StandardMarkdownParser extends MarkdownParser {
  toHTMLString(markdown: string) {
    console.log("Converting markdown content to HTML strings...")
    return markdown;
  }
}

定義轉譯器的抽象裝飾者類別

abstract class MarkdownParserDecorator extends MarkdownParser {
  constructor(protected parser: MarkdownParser){
    super();
  }
  
  abstract extendSyntax(markdown: string): string;
}

定義具體裝飾者來修飾轉譯器的轉譯行為

class CodeBlockDecorator extends MarkdownParserDecorator {
  toHTMLString(markdown: string) {
    const extendedMarkdown = this.extendSyntax(markdown);
    return this.parser.toHTMLString(extendedMarkdown);
  }

  extendSyntax(markdown: string) {
    console.log("Processing code blocks...");
    return markdown;
  }
}

class HighlightDecorator extends MarkdownParserDecorator {
  toHTMLString(markdown: string) {
    const extendedMarkdown = this.extendSyntax(markdown);
    return this.parser.toHTMLString(extendedMarkdown)
  }

  extendSyntax(markdown: string) {
    console.log("Highlighting important notes...");
    return markdown;
  }
}

測試轉譯器

class MarkdownParserTestDrive {
  static main () {
    let parser = new StandardMarkdownParser();
    parser = new CodeBlockDecorator(parser);
    parser = new HighlightDecorator(parser);

    console.log("Start parsing markdown content...");
    parser.toHTMLString("markdown");
  }
}

MarkdownParserTestDrive.main();

定義

Decorator Pattern

  • 裝飾者(Decorator): 為目標物件新增行為或功能
  • 目標物件(Component): 被裝飾的對象

裝飾者模式可以讓程式變得更靈活,因為我們可以透過不同的裝飾者改變物件的行為,而不必變更既有的程式碼。裝飾者可以像堆疊一樣層層相加,不同的搭配會產生不同的結果,從而衍伸出無數的變化。

裝飾者可以動態地修飾目標物件,使用者不需要事先知道物件會如何被裝飾。此外,由於裝飾者類別與被裝飾的物件共享相同的基礎介面,客戶端程式也無需在意面對的是裝飾者或是原來的物件。

總結

  • 裝飾者可以在不修改原有物件的基礎上,在原有行為的前後添加動作或直接取代該行為以擴充或替換原有行為
  • 裝飾者類別和被裝飾的物件通常共享相同的基礎介面或類別,使兩者能夠互換使用
  • 裝飾者可以像堆疊一樣層層相加,使得多個功能可以靈活地組合在一起,以滿足不同需求
  • 將各項功能拆分成不同的裝飾者類別能確保各個類別的職責單一且明確

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/structural/decorator.ts


上一篇
Day 07 - Observer 觀察者
下一篇
Day 09 - Abstract Factory 抽象工廠
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言