2023年7月12日 星期三

C++:簡介 Decorator 模式

本文為 C++ Software Design 書的第 35 節內容。

Decorator 模式的目的是靈活、動態地讓物件加入想要的功能。書中的例子是不同的商品其打折與稅率皆不同,因此需求為解決打折與稅率的問題。Strategy 模式是一個選項,但會有兩個問題:(1) Strategy 模式通常只能支援一個 modifier,也就是說當一個商品同時要打折跟課稅時,或不打折也不課稅時,這些情況都會讓軟體設計變得更困難。以下為書中利用 DecoratedItem 來處理前述情形的例子:

// Abstract class
class Item
{
public:
  virtual ~Item() = default;
  virtual Money price() const = 0;
};

// DecoratedItem class
class DecoratedItem : public Item
{
public:
  explicit DecoratedItem(std::unique_ptr<Item> item) : item_(std::move(item)) {}

protected:
  Item& item() {return *item_;}
  Item const& item() const {return *item_;}

private:
  std::unique_ptr<Item> item_;
};

// Product classes
class Book : public Item {};
class Ticket : public Item {};

// Decorators
class Discounted : public DecoratedItem
{
public:
  Discounted(double discount, std::unique_ptr<Item> item) {}
};
class Taxed : public DecoratedItem
{
public:
  Taxed(double taxRate, std::unique_ptr<Item> item) {}
};

// Usage
int main()
{
  // 7% tax for books
  std::unique_ptr<Item> book(
    std::make_unique<Taxed>(0.07,
      std::make_unique<Book>()));
  // 10% tax and 20% discount for tickets
  std::unique_ptr<Item> ticket(
    std::make_unique<Taxed>(0.10,
      std::make_unique<Discounted>(0.20,
        std::make_unique<Ticket>())));
}
Decorator 與 Adaptor 模式的差別為其目的:Adaptor 模式的目的是轉化一個已有的介面,而 Decorator 目的是擴展及客製化已有的介面。Decorator 與 Strategy 模式較為接近,但前面已提過一個使用 Decorator 的例子。書中的比喻是 Strategy 模式比較像是改變一個類別的核心;而 Decorator 模式比較像是改變一個類別的外殼。 Decorator 模式最主要的問題為一層一層 virtual pointer 造成的效能問題。另外一個要小心的是 Decorator 的順序容易搞錯,像是先課稅再打折或先打折再課稅,都會造成其意義的不同。