今天要介紹的裝飾器模式,跟之前提到過的轉接器模式有點類似(但其實結果完全不一樣)。
轉接器模式的功能在於將讓現有的物件可以透過轉接器,能夠實現自己沒有的介面或功能。而裝飾器的功能在於,維持現有物件既有的介面和功能,但是在執行細節上「加料」。
讓我們來看看下面的例子。假設這裡有個 Printer
的抽象類別,接著 MagazinePrinter
和 BookPrinter
繼承 Printer
並實作 print
方法
abstract class Printer {
abstract print(): void
}
class MagazinePrinter extends Printer {
genre: string;
constructor(genre: string) {
super()
this.genre = genre
}
print(): string {
return `This is a ${this.genre} magazine!`
}
}
class BookPrinter extends Printer {
genre: string;
constructor(genre: string) {
super()
this.genre = genre
}
print(): string {
return `This is a ${this.genre} book!`
}
}
最後,我們就可以建立實例,執行 print
方法得到結果
const economyMagazine = new MagazinePrinter('economy')
const scienceBook = new BookPrinter('science')
economyMagazine.print() // This is a economy magazine!
scienceBook.print() // This is a science book!
這時候書店老闆來了,他說這個月剛好週年慶,因此希望能夠在書印出來之後,也印上 "Best sell!" 的字來刺激買氣。
如果只是因為要為了這一個月的促銷活動,然後建立新的類別,似乎有點大費周章。這時候意旁的工程師說,不如我們就用裝飾器來裝飾原有的物件吧!
這裡我們建立一個 PromotionDecorator
,同樣繼承 Printer
,因此需要實作 print
方法。
不過比較特別的是,這裡我們並不直接實作 print
,而是間接依賴其他 printer 執行 print
方法,然後我們在裝飾器當中加點料,像是下面這樣
class PromotionDecorator extends Printer {
printer: Printer;
constructor(printer: Printer){
super()
this.printer = printer
}
print(): string {
return `${this.printer.print()} (Best sell!)`
}
}
之後我們只要傳入需要被裝飾的實例到裝飾器當中,建立新的、被裝飾過的實例,執行 print
方法之後,就可以得到我們想要的結果囉!
const magazine = new PromotionDecorator(economyMagazine)
const book = new PromotionDecorator(scienceBook)
magazine.print() // This is a economy magazine! (Best sell!)
book.print() // This is a science book! (Best sell!)
使用裝飾器的好處是,可以快速延伸原有物件的功能,而且不用建立任何新的物件。而多個裝飾器可以層層嵌套,因此可以快速建立多樣化的功能。
但反過來說,在層層嵌套的情況下,一旦中間其中一個裝飾器需要變更行為或移除,就會造成一些麻煩。