Skip to content

Design Pattern Implementation

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Implement Singleton thread-safe (Meyer's Singleton)
  • Xây dựng Observer pattern cho event system
  • Sử dụng Strategy pattern với std::function

Mô tả bài tập

Design patterns là giải pháp đã chứng minh cho vấn đề thiết kế phổ biến. Bài tập implement 3 patterns hay dùng nhất trong C++ hiện đại.

Yêu cầu

Bài 1: Singleton — Thread-safe Logger

cpp
class Logger {
public:
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
    static Logger& getInstance() { /* TODO: Meyer's Singleton */ }
    void log(const std::string& level, const std::string& message) {
        // TODO: lock_guard + output
    }
private:
    Logger() = default;
    std::mutex logMutex;
};

Bài 2: Observer — Event System

cpp
class EventListener {
public:
    virtual void onEvent(const std::string& type, const std::string& data) = 0;
    virtual ~EventListener() = default;
};

class EventManager {
    std::map<std::string, std::vector<EventListener*>> listeners;
public:
    void subscribe(const std::string& type, EventListener* l);
    void unsubscribe(const std::string& type, EventListener* l);
    void notify(const std::string& type, const std::string& data);
};

Bài 3: Strategy — Sorting với std::function

cpp
using SortStrategy = std::function<bool(const Product&, const Product&)>;

class ProductSorter {
    SortStrategy strategy;
public:
    void setStrategy(SortStrategy s) { strategy = std::move(s); }
    void sort(std::vector<Product>& products) {
        if (strategy) std::sort(products.begin(), products.end(), strategy);
    }
};
// Dùng lambda cho từng chiến lược: price, rating, name

Gợi ý

Gợi ý Bài 1

Meyer's: static Logger instance; return instance; trong getInstance(). C++11 đảm bảo thread-safe cho local static.

Gợi ý Bài 2

subscribe: push_back. unsubscribe: erase-remove. notify: duyệt listeners[type] gọi onEvent.

Lời giải tham khảo

Xem lời giải
cpp
// Bài 1
Logger& Logger::getInstance() {
    static Logger instance;
    return instance;
}
void Logger::log(const std::string& level, const std::string& message) {
    std::lock_guard<std::mutex> lock(logMutex);
    std::cout << "[" << level << "] " << message << std::endl;
}

// Bài 2
void EventManager::subscribe(const std::string& type, EventListener* l) {
    listeners[type].push_back(l);
}
void EventManager::unsubscribe(const std::string& type, EventListener* l) {
    auto& vec = listeners[type];
    vec.erase(std::remove(vec.begin(), vec.end(), l), vec.end());
}
void EventManager::notify(const std::string& type, const std::string& data) {
    if (listeners.count(type))
        for (auto* l : listeners[type]) l->onEvent(type, data);
}

// Bài 3 — Usage
sorter.setStrategy([](const Product& a, const Product& b) {
    return a.price < b.price;
});