裝飾者模式可以讓你在不改變原有物件的情況下,動態地為物件新增行為或功能。
想像你點了一杯咖啡,這就是一個基本物件。你可以在咖啡上加一些配料,像是鮮奶油或糖漿,但你不需要改變咖啡本身。裝飾者模式就像這些配料,它們可以一層層地加在咖啡上,為咖啡增添風味。但無論你加了什麼,底下的咖啡還是原來那杯,並且隨時可以再加新的配料。
在程式設計中,裝飾者模式能夠讓程式更靈活,因為我們可以在不修改原始碼的情況下為物件加上新功能。這不僅提升了程式的擴展性,還能增加程式的可讀性與維護性。
假設我們有一個 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();
裝飾者模式可以讓程式變得更靈活,因為我們可以透過不同的裝飾者改變物件的行為,而不必變更既有的程式碼。裝飾者可以像堆疊一樣層層相加,不同的搭配會產生不同的結果,從而衍伸出無數的變化。
裝飾者可以動態地修飾目標物件,使用者不需要事先知道物件會如何被裝飾。此外,由於裝飾者類別與被裝飾的物件共享相同的基礎介面,客戶端程式也無需在意面對的是裝飾者或是原來的物件。
https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/structural/decorator.ts