📖
Go C++
  • Introduction
  • Chapter 1: What You Must Know First
    • Virtual Address Space of Process: Memory Partition and Layout
    • Function Call: Stack Frame
    • Program Compiling and Linking
  • Chapter 2: C++ Basics Improvement
    • Default Parameters
    • Inline Function
    • Function Overloading
    • new and delete
    • const and Pointers
    • References in Detail
  • Chapter 3: Object-Oriented Principles
  • Class and Object
  • Constructor and Destructor
  • Shallow Copy and Deep Copy
  • Initializer List
  • Various Member Functions
  • Pointer to Class Members
  • Chapter 4: Template Programming
  • Function Templates
  • Class Templates
  • Memory Allocators
  • Chapter 5: Operator Overloading
    • Operator Overloading
    • Introduction to Iterators
    • Issues of Iterator Invalidation
    • More about new and delete
    • Overloading of new and delete: Object Pool
  • Chapter 6: Inheritance and Polymorphism
    • Look inside Inheritance
    • More about Inheritance
    • Virtual Functions, Static Binding and Dynamic Binding
    • More about Virtual Functions
    • Understanding Polymorphism
    • Abstract Classes
    • Frequently Asked Interview Questions: Polymorphism
  • Chapter 7: Multiple Inheritance
    • Virtual Inheritance and Virtual Base Classes
    • Diamond Problem
    • Four Kinds of Type Conversions
  • Chapter 8: Standard Template Library
    • Sequence Containers
    • Container Adaptors
    • Associative Containers
    • More about Iterators
    • Function Objects
    • Generic Algorithms, Binders and Lambda Expressions
  • Chapter 9: Object Optimization
    • Behind the Object
    • Optimizing Objects in Functions
    • Member Functions with Rvalue References
    • Move Semantics and Perfect Forwarding
  • Chapter 10: Smart Pointers
    • Smart Pointers
    • Smart Pointers without Reference Counting
    • Smart Pointers with Reference Counting
    • Custom Deleters
  • Chapter 11: Function Objects and Binders
    • More about Binders
    • Introduction to std::function
    • Template Specialization and Argument Deduction
    • More about std::function
    • std::bind(): A Simple Thread Pool
    • More about Lambda Expressions
  • Chapter 12: Multithreading
    • Important Features in C++11
    • Multithreaded Programming with std::thread
    • Mutual Exclusion
    • Producer-Consumer Problem
    • Atomic Operations
    • Thread Visibility and volatile
  • Chapter 13: Design Patterns
    • Singleton Pattern
    • Factory Pattern
    • Proxy Pattern
    • Decorator Pattern
    • Adapter Pattern
    • Observer Pattern
Powered by GitBook
On this page

Was this helpful?

  1. Chapter 13: Design Patterns

Decorator Pattern

The decorator pattern enables us to add new functionality to an existing class, without changing its structure. Similar to proxy pattern, the decorator pattern is also a structural pattern, where the decorator class acts as a wrapper for the existing class.

Generally, inheritance is also used to extend the functionality of a class. But as the extended functionality increases, the number of derived classes will expand as well. Therefore, the decorator pattern is more flexible.

Let's look at the following example, where we have an abstract class Car and three brands of cars that implement it and override show() method.

class Car {
public:
    virtual void show() = 0;
};
​
class Bmw : public Car {
public:
    void show() {
        cout << "This is a BMW" << endl;
    }
};
​
class Audi : public Car {
public:
    void show() {
        cout << "This is an Audi" << endl;
    }
};
​
class Benz : public Car {
public:
    void show() {
        cout << "This is a Benz" << endl;
    }
};

Now suppose we want to add new features to these cars. Instead of using inheritance, we can define a decorator class instead. Here we have a class CarDecorator1 which inherits Car. The class has a Car object as its member. The class overrides show(), and besides calling the show() method of the Car object, it implements its own functionality here. This pattern is similar to the proxy pattern, but it actively takes a Car object as a parameter in its constructor. We can also write CarDecorator2 and CarDecorator3 which implements other features in a similar way.

class CarDecorator1 : public Car {
public:
    CarDecorator1(Car *p) : pCar(p) {}
    ~CarDecorator1() {
        delete pCar;
    }
    void show() {
        pCar->show();
        cout << "Feature 1" << endl;
    }
private:
    Car *pCar;
};

To use the decorator, we create a decorator object, and pass in the Car object to be decorated. In this way, different brand of cars can have different features without modifying the original class.

int main() {
    Car *p1 = new CarDecorator1(new Bmw());
    Car *p2 = new CarDecorator2(new Audi());
    Car *p3 = new CarDecorator3(new Benz());
    p1->show();
    p2->show();
    p3->show();
    delete p1;
    delete p2;
    delete p3;
    return 0;
}

The above code has the following output:

This is a Bwm
Feature 1
This is an Audi
Feature 2
This is a Benz
Feature 3

Moreover, a single object can also have multiple features: just use the decorator in a nested way by keep passing the same pointer inside:

int main() {
    Car *p1 = new CarDecorator1(new Bmw());
    p1 = new CarDecorator2(p1);
    p1 = new CarDecorator3(p1);
    p1->show();
    delete p1;
    return 0;
}

The above code has the following output:

This is a Bwm
Feature 1
Feature 2
Feature 3
PreviousProxy PatternNextAdapter Pattern

Last updated 4 years ago

Was this helpful?