iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Software Development

幫自己搞懂物件導向和設計模式系列 第 25

Decorator 裝飾器模式

今天要介紹的裝飾器模式,跟之前提到過的轉接器模式有點類似(但其實結果完全不一樣)。

轉接器模式的功能在於將讓現有的物件可以透過轉接器,能夠實現自己沒有的介面或功能。而裝飾器的功能在於,維持現有物件既有的介面和功能,但是在執行細節上「加料」。

讓我們來看看下面的例子。假設這裡有個 Printer 的抽象類別,接著 MagazinePrinterBookPrinter 繼承 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!)

優點與缺點

使用裝飾器的好處是,可以快速延伸原有物件的功能,而且不用建立任何新的物件。而多個裝飾器可以層層嵌套,因此可以快速建立多樣化的功能。

但反過來說,在層層嵌套的情況下,一旦中間其中一個裝飾器需要變更行為或移除,就會造成一些麻煩。


上一篇
Composite 合成模式
下一篇
Proxy 代理模式
系列文
幫自己搞懂物件導向和設計模式30

尚未有邦友留言

立即登入留言