05_design_patterns.cpp

Download
cpp 356 lines 8.2 KB
  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}