-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecorator.cpp
More file actions
159 lines (128 loc) · 4.72 KB
/
decorator.cpp
File metadata and controls
159 lines (128 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <iostream>
using namespace std;
/*
Decorator Design Pattern Example: Pizza Shop
Use Case:
- Base pizza: Margerita, Farmhouse
- Toppings: Cheese, Paneer
- We want to dynamically add toppings without modifying existing pizza classes
- Instead of creating a class for every combination (Margerita + Cheese + Paneer, etc.),
we use decorators to "wrap" the base pizza and add extra behavior (description & price)
Key Concept:
- Decorator = Inheritance + Composition
* Inheritance: decorator "is-a" Pizza, so it can replace BasePizza anywhere
* Composition: decorator "has-a" Pizza, so it can wrap an existing pizza object
*/
// =========================
// 1️⃣ Base Pizza Interface
// =========================
class BasePizza {
public:
virtual void description() = 0; // description of pizza
virtual int price() = 0; // price of pizza
virtual ~BasePizza() {}
};
// =========================
// 2️⃣ Concrete Pizzas
// =========================
class Margerita : public BasePizza {
int cost;
public:
Margerita(int cost) { this->cost = cost; }
void description() override {
cout << "This is a Margerita Pizza";
}
int price() override { return cost; }
};
class Farmhouse : public BasePizza {
int cost;
public:
Farmhouse(int cost) { this->cost = cost; }
void description() override {
cout << "This is a Farmhouse Pizza";
}
int price() override { return cost; }
};
// =========================
// 3️⃣ Decorator Base Class
// =========================
class PizzaDecorator : public BasePizza {
protected:
BasePizza* pizza; // "wrap" an existing pizza
public:
PizzaDecorator(BasePizza* pizza) { this->pizza = pizza; }
// By default, decorator delegates to the wrapped pizza
void description() override { pizza->description(); }
int price() override { return pizza->price(); }
};
// =========================
// 4️⃣ Concrete Decorators
// =========================
// Cheese Topping Decorator
class CheeseTopping : public PizzaDecorator {
int cost; // extra cost of topping
public:
CheeseTopping(BasePizza* pizza, int cost) : PizzaDecorator(pizza) { this->cost = cost; }
void description() override {
pizza->description(); // first call wrapped pizza description
cout << " with added Cheese Topping"; // then add extra behavior
}
int price() override { return pizza->price() + cost; }
};
// Paneer Topping Decorator
class PaneerTopping : public PizzaDecorator {
int cost; // extra cost of topping
public:
PaneerTopping(BasePizza* pizza, int cost) : PizzaDecorator(pizza) { this->cost = cost; }
void description() override {
pizza->description(); // first call wrapped pizza description
cout << " with added Paneer Topping"; // then add extra behavior
}
int price() override { return pizza->price() + cost; }
};
// =========================
// 5️⃣ Usage / Test
// =========================
int main() {
// Create base pizza
BasePizza* pizza = new Margerita(150);
pizza->description();
cout << " -> Price: " << pizza->price() << endl;
// Add Cheese topping dynamically
pizza = new CheeseTopping(pizza, 10);
pizza->description();
cout << " -> Price: " << pizza->price() << endl;
// Add Paneer topping dynamically
pizza = new PaneerTopping(pizza, 20);
pizza->description();
cout << " -> Price: " << pizza->price() << endl;
// Cleanup (important for memory management)
delete pizza;
return 0;
}
/*
Key Takeaways:
1. Dynamic Behavior:
- We can add multiple toppings at runtime without creating new classes for every combination.
2. Open/Closed Principle:
- BasePizza classes remain unchanged (open for extension, closed for modification)
3. Polymorphism:
- Decorators can be treated as BasePizza objects anywhere in the code.
4. Composition + Inheritance:
- Composition (has-a): decorator contains a pizza object
- Inheritance (is-a): decorator is-a pizza, so it can be used wherever a pizza is expected
5. Real-world Use Cases:
- GUI components:
* Adding scrollbars, borders, or theming dynamically to windows/widgets
- File I/O:
* Adding compression, encryption, or buffering to input/output streams
- Logging:
* Adding timestamp, log level, or file/console output decorators to logger objects
- Notifications:
* Wrapping notification system to add Email, SMS, or Push without modifying core logic
- Middleware in web frameworks:
* Adding authentication, caching, or logging layers dynamically to HTTP request handlers
- Any situation where:
* You want to add responsibilities **dynamically at runtime**
* You want to avoid an explosion of subclasses for every combination of features
*/