1/*
2 * Design Patterns in C++ Demo
3 *
4 * Demonstrates:
5 * - Singleton (thread-safe Meyer's)
6 * - Observer pattern with std::function
7 * - Strategy pattern with lambdas
8 * - RAII / Resource management
9 * - CRTP (Curiously Recurring Template Pattern)
10 *
11 * Compile: g++ -std=c++20 -Wall -Wextra 05_design_patterns.cpp -o design_patterns
12 */
13
14#include <iostream>
15#include <vector>
16#include <functional>
17#include <memory>
18#include <string>
19#include <algorithm>
20#include <fstream>
21
22// ============ Singleton Pattern (Thread-Safe Meyer's) ============
23class Logger {
24private:
25 Logger() {
26 std::cout << "[Logger initialized]\n";
27 }
28
29public:
30 // Delete copy/move
31 Logger(const Logger&) = delete;
32 Logger& operator=(const Logger&) = delete;
33 Logger(Logger&&) = delete;
34 Logger& operator=(Logger&&) = delete;
35
36 static Logger& instance() {
37 static Logger instance; // Thread-safe in C++11+
38 return instance;
39 }
40
41 void log(const std::string& message) {
42 std::cout << "[LOG] " << message << "\n";
43 }
44};
45
46void demo_singleton() {
47 std::cout << "\n=== Singleton Pattern ===\n";
48
49 Logger::instance().log("First message");
50 Logger::instance().log("Second message");
51
52 Logger& logger = Logger::instance();
53 logger.log("Third message");
54}
55
56// ============ Observer Pattern ============
57class Subject {
58private:
59 std::vector<std::function<void(const std::string&)>> observers_;
60
61public:
62 void attach(std::function<void(const std::string&)> observer) {
63 observers_.push_back(observer);
64 }
65
66 void notify(const std::string& event) {
67 for (auto& observer : observers_) {
68 observer(event);
69 }
70 }
71};
72
73void demo_observer() {
74 std::cout << "\n=== Observer Pattern ===\n";
75
76 Subject subject;
77
78 // Attach observers using lambdas
79 subject.attach([](const std::string& event) {
80 std::cout << "Observer 1 received: " << event << "\n";
81 });
82
83 subject.attach([](const std::string& event) {
84 std::cout << "Observer 2 received: " << event << "\n";
85 });
86
87 // Capture state in observer
88 int count = 0;
89 subject.attach([&count](const std::string& event) {
90 count++;
91 std::cout << "Observer 3 received (count=" << count << "): " << event << "\n";
92 });
93
94 // Notify all observers
95 subject.notify("Button clicked");
96 subject.notify("Data updated");
97}
98
99// ============ Strategy Pattern ============
100class SortStrategy {
101public:
102 virtual ~SortStrategy() = default;
103 virtual void sort(std::vector<int>& data) = 0;
104};
105
106class BubbleSort : public SortStrategy {
107public:
108 void sort(std::vector<int>& data) override {
109 std::cout << " Using bubble sort\n";
110 for (size_t i = 0; i < data.size(); i++) {
111 for (size_t j = 0; j < data.size() - i - 1; j++) {
112 if (data[j] > data[j + 1]) {
113 std::swap(data[j], data[j + 1]);
114 }
115 }
116 }
117 }
118};
119
120class QuickSort : public SortStrategy {
121public:
122 void sort(std::vector<int>& data) override {
123 std::cout << " Using quick sort (std::sort)\n";
124 std::sort(data.begin(), data.end());
125 }
126};
127
128class Sorter {
129private:
130 std::unique_ptr<SortStrategy> strategy_;
131
132public:
133 void set_strategy(std::unique_ptr<SortStrategy> strategy) {
134 strategy_ = std::move(strategy);
135 }
136
137 void sort(std::vector<int>& data) {
138 if (strategy_) {
139 strategy_->sort(data);
140 }
141 }
142};
143
144// Strategy with lambdas (modern approach)
145using SortFunc = std::function<void(std::vector<int>&)>;
146
147void demo_strategy() {
148 std::cout << "\n=== Strategy Pattern ===\n";
149
150 std::vector<int> data1 = {5, 2, 8, 1, 9};
151 std::vector<int> data2 = {5, 2, 8, 1, 9};
152
153 // Classic approach
154 Sorter sorter;
155 sorter.set_strategy(std::make_unique<BubbleSort>());
156 sorter.sort(data1);
157
158 sorter.set_strategy(std::make_unique<QuickSort>());
159 sorter.sort(data2);
160
161 // Lambda-based approach
162 std::cout << "\nLambda-based strategy:\n";
163 std::vector<int> data3 = {5, 2, 8, 1, 9};
164
165 SortFunc reverse_sort = [](std::vector<int>& data) {
166 std::cout << " Using reverse sort\n";
167 std::sort(data.begin(), data.end(), std::greater<int>());
168 };
169
170 reverse_sort(data3);
171
172 std::cout << " Sorted (descending): ";
173 for (int n : data3) {
174 std::cout << n << " ";
175 }
176 std::cout << "\n";
177}
178
179// ============ RAII (Resource Acquisition Is Initialization) ============
180class FileHandler {
181private:
182 std::ofstream file_;
183 std::string filename_;
184
185public:
186 FileHandler(const std::string& filename) : filename_(filename) {
187 file_.open(filename);
188 std::cout << " [File '" << filename_ << "' opened]\n";
189 }
190
191 ~FileHandler() {
192 if (file_.is_open()) {
193 file_.close();
194 std::cout << " [File '" << filename_ << "' closed]\n";
195 }
196 }
197
198 // Delete copy, allow move
199 FileHandler(const FileHandler&) = delete;
200 FileHandler& operator=(const FileHandler&) = delete;
201
202 FileHandler(FileHandler&& other) noexcept
203 : file_(std::move(other.file_)), filename_(std::move(other.filename_)) {
204 }
205
206 void write(const std::string& data) {
207 if (file_.is_open()) {
208 file_ << data;
209 }
210 }
211};
212
213void demo_raii() {
214 std::cout << "\n=== RAII Pattern ===\n";
215
216 {
217 FileHandler file("/tmp/raii_test.txt");
218 file.write("Hello RAII!\n");
219 file.write("Resource automatically managed.\n");
220
221 std::cout << " Using file...\n";
222 // File automatically closed when leaving scope
223 }
224
225 std::cout << " File closed automatically\n";
226}
227
228// ============ CRTP (Curiously Recurring Template Pattern) ============
229template<typename Derived>
230class Shape {
231public:
232 void draw() const {
233 static_cast<const Derived*>(this)->draw_impl();
234 }
235
236 double area() const {
237 return static_cast<const Derived*>(this)->area_impl();
238 }
239};
240
241class Circle : public Shape<Circle> {
242private:
243 double radius_;
244
245public:
246 Circle(double r) : radius_(r) {}
247
248 void draw_impl() const {
249 std::cout << " Drawing Circle (radius=" << radius_ << ")\n";
250 }
251
252 double area_impl() const {
253 return 3.14159 * radius_ * radius_;
254 }
255};
256
257class Rectangle : public Shape<Rectangle> {
258private:
259 double width_, height_;
260
261public:
262 Rectangle(double w, double h) : width_(w), height_(h) {}
263
264 void draw_impl() const {
265 std::cout << " Drawing Rectangle (" << width_ << "x" << height_ << ")\n";
266 }
267
268 double area_impl() const {
269 return width_ * height_;
270 }
271};
272
273template<typename T>
274void process_shape(const Shape<T>& shape) {
275 shape.draw();
276 std::cout << " Area: " << shape.area() << "\n";
277}
278
279void demo_crtp() {
280 std::cout << "\n=== CRTP (Curiously Recurring Template Pattern) ===\n";
281
282 Circle circle(5.0);
283 Rectangle rect(4.0, 6.0);
284
285 process_shape(circle);
286 process_shape(rect);
287
288 std::cout << " CRTP enables static polymorphism (no virtual calls)\n";
289}
290
291// ============ Factory Pattern ============
292class Animal {
293public:
294 virtual ~Animal() = default;
295 virtual void make_sound() const = 0;
296};
297
298class Dog : public Animal {
299public:
300 void make_sound() const override {
301 std::cout << " Woof!\n";
302 }
303};
304
305class Cat : public Animal {
306public:
307 void make_sound() const override {
308 std::cout << " Meow!\n";
309 }
310};
311
312class AnimalFactory {
313public:
314 static std::unique_ptr<Animal> create(const std::string& type) {
315 if (type == "dog") {
316 return std::make_unique<Dog>();
317 } else if (type == "cat") {
318 return std::make_unique<Cat>();
319 }
320 return nullptr;
321 }
322};
323
324void demo_factory() {
325 std::cout << "\n=== Factory Pattern ===\n";
326
327 auto dog = AnimalFactory::create("dog");
328 auto cat = AnimalFactory::create("cat");
329
330 if (dog) {
331 std::cout << "Dog says: ";
332 dog->make_sound();
333 }
334
335 if (cat) {
336 std::cout << "Cat says: ";
337 cat->make_sound();
338 }
339}
340
341// ============ Main ============
342int main() {
343 std::cout << "Design Patterns in C++ Demo\n";
344 std::cout << "============================\n";
345
346 demo_singleton();
347 demo_observer();
348 demo_strategy();
349 demo_raii();
350 demo_crtp();
351 demo_factory();
352
353 std::cout << "\nAll demos completed!\n";
354 return 0;
355}