🏗️ Design Patterns trong C++
📖 Giới thiệu
Design Patterns là những giải pháp tái sử dụng cho các vấn đề phổ biến trong thiết kế phần mềm. Chúng là những "công thức" đã được kiểm chứng để giải quyết các thách thức lập trình thường gặp, giúp code trở nên dễ hiểu, dễ bảo trì và có thể mở rộng.
Tại sao cần Design Patterns?
- Code Reusability: Tái sử dụng giải pháp đã được kiểm chứng
- Communication: Ngôn ngữ chung giữa developers
- Best Practices: Áp dụng các practices tốt nhất
- Maintainability: Code dễ bảo trì và mở rộng
Ba nhóm chính:
- Creational Patterns: Quản lý việc tạo objects
- Structural Patterns: Quản lý cách objects tương tác
- Behavioral Patterns: Quản lý communication giữa objects
Modern C++ Features:
- Smart Pointers: Thay thế raw pointers
- RAII: Resource management tự động
- Templates: Generic programming
- Lambda Functions: Functional programming patterns
- Move Semantics: Efficient resource transfer
🔧 Cú pháp
Singleton Pattern
#include <iostream>
#include <memory>
#include <mutex>
using namespace std;
// Thread-safe Singleton với C++11
class Logger {
private:
static unique_ptr<Logger> instance_;
static once_flag initialized_;
string logFile_;
mutex logMutex_;
// Private constructor
Logger(const string& filename = "app.log") : logFile_(filename) {
cout << "📋 Logger initialized with file: " << logFile_ << endl;
}
public:
// Delete copy constructor và assignment
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
// Thread-safe instance creation
static Logger& getInstance() {
call_once(initialized_, []() {
instance_ = unique_ptr<Logger>(new Logger());
});
return *instance_;
}
void log(const string& message) {
lock_guard<mutex> lock(logMutex_);
cout << "[LOG] " << message << endl;
// Write to file...
}
void setLogFile(const string& filename) {
lock_guard<mutex> lock(logMutex_);
logFile_ = filename;
cout << "📋 Log file changed to: " << logFile_ << endl;
}
};
// Static member definitions
unique_ptr<Logger> Logger::instance_ = nullptr;
once_flag Logger::initialized_;Factory Pattern
#include <memory>
#include <string>
#include <map>
#include <functional>
// Abstract Product
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
virtual double getArea() const = 0;
virtual string getType() const = 0;
};
// Concrete Products
class Circle : public Shape {
private:
double radius_;
public:
Circle(double radius = 1.0) : radius_(radius) {}
void draw() const override {
cout << "🔵 Drawing Circle with radius " << radius_ << endl;
}
double getArea() const override {
return 3.14159 * radius_ * radius_;
}
string getType() const override { return "Circle"; }
};
class Rectangle : public Shape {
private:
double width_, height_;
public:
Rectangle(double width = 1.0, double height = 1.0)
: width_(width), height_(height) {}
void draw() const override {
cout << "🔲 Drawing Rectangle " << width_ << "x" << height_ << endl;
}
double getArea() const override {
return width_ * height_;
}
string getType() const override { return "Rectangle"; }
};
// Factory with lambda-based creators
class ShapeFactory {
private:
using Creator = function<unique_ptr<Shape>(const vector<double>&)>;
map<string, Creator> creators_;
public:
ShapeFactory() {
// Register creators using lambdas
creators_["circle"] = [](const vector<double>& params) -> unique_ptr<Shape> {
double radius = params.empty() ? 1.0 : params[0];
return make_unique<Circle>(radius);
};
creators_["rectangle"] = [](const vector<double>& params) -> unique_ptr<Shape> {
double width = params.size() > 0 ? params[0] : 1.0;
double height = params.size() > 1 ? params[1] : 1.0;
return make_unique<Rectangle>(width, height);
};
}
unique_ptr<Shape> createShape(const string& type, const vector<double>& params = {}) {
auto it = creators_.find(type);
if (it != creators_.end()) {
return it->second(params);
}
cout << "❌ Unknown shape type: " << type << endl;
return nullptr;
}
vector<string> getAvailableTypes() const {
vector<string> types;
for (const auto& pair : creators_) {
types.push_back(pair.first);
}
return types;
}
};Observer Pattern
#include <vector>
#include <algorithm>
#include <memory>
// Observer interface
template<typename T>
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const T& data) = 0;
};
// Subject class
template<typename T>
class Subject {
private:
vector<weak_ptr<Observer<T>>> observers_;
T data_;
public:
void attach(shared_ptr<Observer<T>> observer) {
observers_.push_back(observer);
cout << "👁️ Observer attached (total: " << observers_.size() << ")" << endl;
}
void detach(shared_ptr<Observer<T>> observer) {
observers_.erase(
remove_if(observers_.begin(), observers_.end(),
[&observer](const weak_ptr<Observer<T>>& weak_obs) {
return weak_obs.lock() == observer;
}),
observers_.end()
);
cout << "👁️ Observer detached" << endl;
}
void notify() {
cout << "📢 Notifying " << observers_.size() << " observers..." << endl;
// Clean up expired observers while notifying
auto it = observers_.begin();
while (it != observers_.end()) {
if (auto observer = it->lock()) {
observer->update(data_);
++it;
} else {
cout << "🧹 Removing expired observer" << endl;
it = observers_.erase(it);
}
}
}
void setData(const T& data) {
data_ = data;
notify();
}
const T& getData() const { return data_; }
size_t getObserverCount() const { return observers_.size(); }
};Strategy Pattern
#include <functional>
// Strategy interface using std::function
template<typename T>
using SortStrategy = function<void(vector<T>&)>;
// Strategy implementations
class SortStrategies {
public:
template<typename T>
static SortStrategy<T> bubbleSort() {
return [](vector<T>& data) {
cout << "🫧 Using Bubble Sort" << endl;
for (size_t i = 0; i < data.size(); i++) {
for (size_t j = 0; j < data.size() - i - 1; j++) {
if (data[j] > data[j + 1]) {
swap(data[j], data[j + 1]);
}
}
}
};
}
template<typename T>
static SortStrategy<T> quickSort() {
return [](vector<T>& data) {
cout << "⚡ Using Quick Sort" << endl;
sort(data.begin(), data.end());
};
}
template<typename T>
static SortStrategy<T> customSort(function<bool(const T&, const T&)> comparator) {
return [comparator](vector<T>& data) {
cout << "🎯 Using Custom Sort" << endl;
sort(data.begin(), data.end(), comparator);
};
}
};
// Context class
template<typename T>
class Sorter {
private:
SortStrategy<T> strategy_;
public:
Sorter(SortStrategy<T> strategy) : strategy_(strategy) {}
void setStrategy(SortStrategy<T> strategy) {
strategy_ = strategy;
cout << "🔄 Strategy changed" << endl;
}
void sort(vector<T>& data) {
if (strategy_) {
strategy_(data);
} else {
cout << "❌ No strategy set!" << endl;
}
}
};🔬 Phân tích & Giải thích chi tiết
Khi nào sử dụng từng Pattern?
Singleton Pattern:
- Sử dụng khi: Cần đảm bảo chỉ có một instance (Logger, Database connection)
- Ưu điểm: Global access point, lazy initialization
- Nhược điểm: Khó test, tight coupling, thread safety issues
- Modern C++ approach: std::once_flag, smart pointers
Factory Pattern:
- Sử dụng khi: Cần tạo objects mà không biết trước concrete type
- Ưu điểm: Loose coupling, easy to extend
- Nhược điểm: Complexity tăng
- Modern C++ approach: std::function, lambdas, perfect forwarding
Observer Pattern:
- Sử dụng khi: Một object thay đổi cần notify nhiều objects khác
- Ưu điểm: Loose coupling, dynamic relationships
- Nhược điểm: Memory leaks nếu không cleanup
- Modern C++ approach: weak_ptr để tránh circular references
Strategy Pattern:
- Sử dụng khi: Cần thay đổi algorithm runtime
- Ưu điểm: Open/Closed principle, testable
- Nhược điểm: Increased number of objects
- Modern C++ approach: std::function, lambdas thay vì inheritance
Modern C++ Improvements
Traditional vs Modern Implementation:
// Traditional approach với inheritance
class OldStrategy {
public:
virtual ~OldStrategy() = default;
virtual void execute() = 0;
};
class ConcreteStrategy : public OldStrategy {
public:
void execute() override { /* implementation */ }
};
// Modern approach với std::function
using ModernStrategy = function<void()>;
ModernStrategy strategy = []() { /* implementation */ };Benefits của Modern Approach:
- Ít boilerplate code
- Dễ compose và combine
- Functional programming style
- Better performance (no virtual calls)
💻 Ví dụ minh họa
Ví dụ 1: Event System với Observer Pattern
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <map>
#include <functional>
#include <algorithm>
using namespace std;
// Event data structures
struct GameEvent {
string type;
map<string, string> data;
GameEvent(const string& t) : type(t) {}
GameEvent& with(const string& key, const string& value) {
data[key] = value;
return *this;
}
};
// Modern Observer interface
class EventObserver {
public:
virtual ~EventObserver() = default;
virtual void onEvent(const GameEvent& event) = 0;
virtual string getObserverName() const = 0;
};
// Event Manager (Subject)
class EventManager {
private:
map<string, vector<weak_ptr<EventObserver>>> observers_;
vector<GameEvent> eventHistory_;
public:
void subscribe(const string& eventType, shared_ptr<EventObserver> observer) {
observers_[eventType].push_back(observer);
cout << "📮 " << observer->getObserverName()
<< " subscribed to " << eventType << endl;
}
void unsubscribe(const string& eventType, shared_ptr<EventObserver> observer) {
auto& eventObservers = observers_[eventType];
eventObservers.erase(
remove_if(eventObservers.begin(), eventObservers.end(),
[&observer](const weak_ptr<EventObserver>& weak_obs) {
return weak_obs.lock() == observer;
}),
eventObservers.end()
);
cout << "📮 " << observer->getObserverName()
<< " unsubscribed from " << eventType << endl;
}
void publish(const GameEvent& event) {
cout << "\n📢 Publishing event: " << event.type << endl;
eventHistory_.push_back(event);
auto it = observers_.find(event.type);
if (it != observers_.end()) {
auto& eventObservers = it->second;
// Notify observers and clean up expired ones
auto obsIt = eventObservers.begin();
while (obsIt != eventObservers.end()) {
if (auto observer = obsIt->lock()) {
observer->onEvent(event);
++obsIt;
} else {
cout << "🧹 Removing expired observer for " << event.type << endl;
obsIt = eventObservers.erase(obsIt);
}
}
} else {
cout << "📭 No subscribers for event: " << event.type << endl;
}
}
void printStats() const {
cout << "\n📊 EVENT MANAGER STATS:" << endl;
cout << "Event types: " << observers_.size() << endl;
cout << "Total events published: " << eventHistory_.size() << endl;
for (const auto& pair : observers_) {
size_t activeObservers = 0;
for (const auto& weak_obs : pair.second) {
if (!weak_obs.expired()) activeObservers++;
}
cout << "- " << pair.first << ": " << activeObservers << " active observers" << endl;
}
}
};
// Concrete Observers
class PlayerController : public EventObserver {
private:
string playerName_;
int health_;
int score_;
public:
PlayerController(const string& name) : playerName_(name), health_(100), score_(0) {}
void onEvent(const GameEvent& event) override {
if (event.type == "enemy_defeated") {
score_ += 10;
cout << "🎮 " << playerName_ << " gained points! Score: " << score_ << endl;
} else if (event.type == "player_damaged") {
auto it = event.data.find("player");
if (it != event.data.end() && it->second == playerName_) {
health_ -= 10;
cout << "🎮 " << playerName_ << " took damage! Health: " << health_ << endl;
}
} else if (event.type == "game_over") {
cout << "🎮 " << playerName_ << " final score: " << score_ << endl;
}
}
string getObserverName() const override {
return "PlayerController[" + playerName_ + "]";
}
int getHealth() const { return health_; }
int getScore() const { return score_; }
};
class SoundManager : public EventObserver {
private:
bool soundEnabled_;
vector<string> playedSounds_;
public:
SoundManager(bool enabled = true) : soundEnabled_(enabled) {}
void onEvent(const GameEvent& event) override {
if (!soundEnabled_) return;
string soundFile;
if (event.type == "enemy_defeated") {
soundFile = "victory.wav";
} else if (event.type == "player_damaged") {
soundFile = "hurt.wav";
} else if (event.type == "game_over") {
soundFile = "game_over.wav";
} else if (event.type == "level_completed") {
soundFile = "level_complete.wav";
}
if (!soundFile.empty()) {
playedSounds_.push_back(soundFile);
cout << "🔊 Playing sound: " << soundFile << endl;
}
}
string getObserverName() const override {
return "SoundManager";
}
void enableSound(bool enabled) { soundEnabled_ = enabled; }
void printSoundHistory() const {
cout << "🔊 Sounds played: ";
for (const auto& sound : playedSounds_) {
cout << sound << " ";
}
cout << endl;
}
};
class UIManager : public EventObserver {
private:
string currentScreen_;
int notifications_;
public:
UIManager() : currentScreen_("main_menu"), notifications_(0) {}
void onEvent(const GameEvent& event) override {
if (event.type == "enemy_defeated") {
notifications_++;
cout << "🖥️ UI: Enemy defeated notification shown" << endl;
} else if (event.type == "player_damaged") {
cout << "🖥️ UI: Health bar updated" << endl;
} else if (event.type == "game_over") {
currentScreen_ = "game_over";
cout << "🖥️ UI: Switched to game over screen" << endl;
} else if (event.type == "level_completed") {
currentScreen_ = "level_complete";
cout << "🖥️ UI: Switched to level complete screen" << endl;
}
}
string getObserverName() const override {
return "UIManager";
}
string getCurrentScreen() const { return currentScreen_; }
int getNotificationCount() const { return notifications_; }
};
// Game Simulation
class GameSimulator {
private:
EventManager eventManager_;
shared_ptr<PlayerController> player_;
shared_ptr<SoundManager> soundManager_;
shared_ptr<UIManager> uiManager_;
public:
GameSimulator() {
// Create observers
player_ = make_shared<PlayerController>("Hero");
soundManager_ = make_shared<SoundManager>(true);
uiManager_ = make_shared<UIManager>();
// Subscribe to events
eventManager_.subscribe("enemy_defeated", player_);
eventManager_.subscribe("player_damaged", player_);
eventManager_.subscribe("game_over", player_);
eventManager_.subscribe("enemy_defeated", soundManager_);
eventManager_.subscribe("player_damaged", soundManager_);
eventManager_.subscribe("game_over", soundManager_);
eventManager_.subscribe("level_completed", soundManager_);
eventManager_.subscribe("enemy_defeated", uiManager_);
eventManager_.subscribe("player_damaged", uiManager_);
eventManager_.subscribe("game_over", uiManager_);
eventManager_.subscribe("level_completed", uiManager_);
cout << "🎮 Game initialized with all systems" << endl;
}
void simulateGameplay() {
cout << "\n=== GAME SIMULATION START ===" << endl;
// Player defeats enemies
eventManager_.publish(GameEvent("enemy_defeated")
.with("enemy_type", "goblin")
.with("location", "forest"));
eventManager_.publish(GameEvent("enemy_defeated")
.with("enemy_type", "orc")
.with("location", "cave"));
// Player takes damage
eventManager_.publish(GameEvent("player_damaged")
.with("player", "Hero")
.with("damage", "10"));
// More enemies defeated
eventManager_.publish(GameEvent("enemy_defeated")
.with("enemy_type", "dragon")
.with("location", "castle"));
// Level completed
eventManager_.publish(GameEvent("level_completed")
.with("level", "1")
.with("time", "300"));
// Player takes more damage
for (int i = 0; i < 9; i++) {
eventManager_.publish(GameEvent("player_damaged")
.with("player", "Hero")
.with("damage", "10"));
}
// Game over
eventManager_.publish(GameEvent("game_over")
.with("reason", "player_died"));
cout << "\n=== GAME SIMULATION END ===" << endl;
}
void printGameStats() {
cout << "\n📊 GAME STATS:" << endl;
cout << "Player health: " << player_->getHealth() << endl;
cout << "Player score: " << player_->getScore() << endl;
cout << "Current UI screen: " << uiManager_->getCurrentScreen() << endl;
cout << "UI notifications: " << uiManager_->getNotificationCount() << endl;
soundManager_->printSoundHistory();
eventManager_.printStats();
}
void testObserverLifetime() {
cout << "\n=== OBSERVER LIFETIME TEST ===" << endl;
// Create temporary observer
{
auto tempObserver = make_shared<SoundManager>(false);
eventManager_.subscribe("test_event", tempObserver);
eventManager_.publish(GameEvent("test_event"));
cout << "🔄 Temporary observer going out of scope..." << endl;
} // tempObserver destroyed here
// Publish another event to trigger cleanup
eventManager_.publish(GameEvent("test_event"));
eventManager_.printStats();
}
};
int main() {
cout << "=== MODERN OBSERVER PATTERN DEMO ===" << endl;
GameSimulator game;
game.simulateGameplay();
game.printGameStats();
game.testObserverLifetime();
return 0;
}Ví dụ 2: Plugin System với Factory Pattern
#include <iostream>
#include <memory>
#include <map>
#include <string>
#include <vector>
#include <functional>
#include <dlfcn.h> // For dynamic loading (Linux/macOS)
using namespace std;
// Plugin interface
class Plugin {
public:
virtual ~Plugin() = default;
virtual string getName() const = 0;
virtual string getVersion() const = 0;
virtual void initialize() = 0;
virtual void execute(const map<string, string>& parameters) = 0;
virtual void shutdown() = 0;
};
// Plugin metadata
struct PluginMetadata {
string name;
string version;
string description;
vector<string> dependencies;
PluginMetadata(const string& n, const string& v, const string& desc = "")
: name(n), version(v), description(desc) {}
};
// Plugin Factory với registration system
class PluginFactory {
private:
using PluginCreator = function<unique_ptr<Plugin>()>;
map<string, PluginCreator> creators_;
map<string, PluginMetadata> metadata_;
public:
// Register plugin creator
template<typename T>
void registerPlugin(const PluginMetadata& metadata) {
creators_[metadata.name] = []() -> unique_ptr<Plugin> {
return make_unique<T>();
};
metadata_[metadata.name] = metadata;
cout << "🔌 Registered plugin: " << metadata.name
<< " v" << metadata.version << endl;
}
// Create plugin instance
unique_ptr<Plugin> createPlugin(const string& name) {
auto it = creators_.find(name);
if (it != creators_.end()) {
cout << "🏭 Creating plugin: " << name << endl;
return it->second();
}
cout << "❌ Plugin not found: " << name << endl;
return nullptr;
}
// Get available plugins
vector<string> getAvailablePlugins() const {
vector<string> plugins;
for (const auto& pair : creators_) {
plugins.push_back(pair.first);
}
return plugins;
}
// Get plugin metadata
const PluginMetadata* getMetadata(const string& name) const {
auto it = metadata_.find(name);
return (it != metadata_.end()) ? &it->second : nullptr;
}
void printAvailablePlugins() const {
cout << "\n📋 AVAILABLE PLUGINS:" << endl;
for (const auto& pair : metadata_) {
const auto& meta = pair.second;
cout << "🔌 " << meta.name << " v" << meta.version << endl;
cout << " Description: " << meta.description << endl;
if (!meta.dependencies.empty()) {
cout << " Dependencies: ";
for (const auto& dep : meta.dependencies) {
cout << dep << " ";
}
cout << endl;
}
cout << endl;
}
}
};
// Concrete Plugin Implementations
class LoggerPlugin : public Plugin {
private:
bool initialized_;
string logLevel_;
public:
LoggerPlugin() : initialized_(false), logLevel_("INFO") {}
string getName() const override { return "Logger"; }
string getVersion() const override { return "1.0.0"; }
void initialize() override {
initialized_ = true;
cout << "📋 Logger Plugin initialized" << endl;
}
void execute(const map<string, string>& params) override {
if (!initialized_) {
cout << "❌ Logger Plugin not initialized!" << endl;
return;
}
auto levelIt = params.find("level");
if (levelIt != params.end()) {
logLevel_ = levelIt->second;
}
auto messageIt = params.find("message");
string message = (messageIt != params.end()) ? messageIt->second : "Default log message";
cout << "[" << logLevel_ << "] " << message << endl;
}
void shutdown() override {
initialized_ = false;
cout << "📋 Logger Plugin shut down" << endl;
}
};
class DatabasePlugin : public Plugin {
private:
bool connected_;
string connectionString_;
public:
DatabasePlugin() : connected_(false) {}
string getName() const override { return "Database"; }
string getVersion() const override { return "2.1.0"; }
void initialize() override {
connectionString_ = "localhost:5432/appdb";
connected_ = true;
cout << "🗄️ Database Plugin connected to " << connectionString_ << endl;
}
void execute(const map<string, string>& params) override {
if (!connected_) {
cout << "❌ Database Plugin not connected!" << endl;
return;
}
auto queryIt = params.find("query");
if (queryIt != params.end()) {
cout << "🗄️ Executing query: " << queryIt->second << endl;
cout << "🗄️ Query executed successfully (simulated)" << endl;
} else {
cout << "❌ No query provided to Database Plugin" << endl;
}
}
void shutdown() override {
connected_ = false;
cout << "🗄️ Database Plugin disconnected" << endl;
}
};
class EmailPlugin : public Plugin {
private:
bool configured_;
string smtpServer_;
public:
EmailPlugin() : configured_(false) {}
string getName() const override { return "Email"; }
string getVersion() const override { return "1.5.2"; }
void initialize() override {
smtpServer_ = "smtp.gmail.com:587";
configured_ = true;
cout << "📧 Email Plugin configured with " << smtpServer_ << endl;
}
void execute(const map<string, string>& params) override {
if (!configured_) {
cout << "❌ Email Plugin not configured!" << endl;
return;
}
auto toIt = params.find("to");
auto subjectIt = params.find("subject");
auto bodyIt = params.find("body");
string to = (toIt != params.end()) ? toIt->second : "unknown@example.com";
string subject = (subjectIt != params.end()) ? subjectIt->second : "No Subject";
string body = (bodyIt != params.end()) ? bodyIt->second : "Empty message";
cout << "📧 Sending email:" << endl;
cout << " To: " << to << endl;
cout << " Subject: " << subject << endl;
cout << " Body: " << body << endl;
cout << "📧 Email sent successfully (simulated)" << endl;
}
void shutdown() override {
configured_ = false;
cout << "📧 Email Plugin shut down" << endl;
}
};
// Plugin Manager
class PluginManager {
private:
PluginFactory factory_;
map<string, unique_ptr<Plugin>> loadedPlugins_;
vector<string> loadOrder_;
public:
PluginManager() {
// Register built-in plugins
factory_.registerPlugin<LoggerPlugin>(
PluginMetadata("Logger", "1.0.0", "Logging functionality")
);
factory_.registerPlugin<DatabasePlugin>(
PluginMetadata("Database", "2.1.0", "Database operations")
);
factory_.registerPlugin<EmailPlugin>(
PluginMetadata("Email", "1.5.2", "Email notifications")
);
cout << "🔌 PluginManager initialized" << endl;
}
~PluginManager() {
shutdown();
cout << "🔌 PluginManager destroyed" << endl;
}
bool loadPlugin(const string& name) {
if (loadedPlugins_.find(name) != loadedPlugins_.end()) {
cout << "⚠️ Plugin already loaded: " << name << endl;
return true;
}
auto plugin = factory_.createPlugin(name);
if (plugin) {
try {
plugin->initialize();
loadedPlugins_[name] = move(plugin);
loadOrder_.push_back(name);
cout << "✅ Plugin loaded: " << name << endl;
return true;
} catch (const exception& e) {
cout << "❌ Failed to initialize plugin " << name << ": " << e.what() << endl;
}
}
return false;
}
void unloadPlugin(const string& name) {
auto it = loadedPlugins_.find(name);
if (it != loadedPlugins_.end()) {
try {
it->second->shutdown();
loadedPlugins_.erase(it);
// Remove from load order
loadOrder_.erase(
remove(loadOrder_.begin(), loadOrder_.end(), name),
loadOrder_.end()
);
cout << "🔄 Plugin unloaded: " << name << endl;
} catch (const exception& e) {
cout << "❌ Error unloading plugin " << name << ": " << e.what() << endl;
}
} else {
cout << "⚠️ Plugin not loaded: " << name << endl;
}
}
bool executePlugin(const string& name, const map<string, string>& params) {
auto it = loadedPlugins_.find(name);
if (it != loadedPlugins_.end()) {
try {
it->second->execute(params);
return true;
} catch (const exception& e) {
cout << "❌ Error executing plugin " << name << ": " << e.what() << endl;
}
} else {
cout << "❌ Plugin not loaded: " << name << endl;
}
return false;
}
void shutdown() {
cout << "🔄 Shutting down all plugins..." << endl;
// Shutdown in reverse order
for (auto it = loadOrder_.rbegin(); it != loadOrder_.rend(); ++it) {
unloadPlugin(*it);
}
}
void printLoadedPlugins() const {
cout << "\n📋 LOADED PLUGINS:" << endl;
if (loadedPlugins_.empty()) {
cout << "No plugins loaded" << endl;
return;
}
for (const auto& name : loadOrder_) {
auto it = loadedPlugins_.find(name);
if (it != loadedPlugins_.end()) {
cout << "🔌 " << it->second->getName()
<< " v" << it->second->getVersion() << endl;
}
}
}
void printAvailablePlugins() const {
factory_.printAvailablePlugins();
}
};
// Application using plugin system
class Application {
private:
PluginManager pluginManager_;
public:
void run() {
cout << "=== PLUGIN SYSTEM DEMO ===" << endl;
// Show available plugins
pluginManager_.printAvailablePlugins();
// Load plugins
cout << "\n=== LOADING PLUGINS ===" << endl;
pluginManager_.loadPlugin("Logger");
pluginManager_.loadPlugin("Database");
pluginManager_.loadPlugin("Email");
pluginManager_.printLoadedPlugins();
// Use plugins
cout << "\n=== USING PLUGINS ===" << endl;
// Logger plugin
pluginManager_.executePlugin("Logger", {
{"level", "INFO"},
{"message", "Application started"}
});
pluginManager_.executePlugin("Logger", {
{"level", "ERROR"},
{"message", "Something went wrong"}
});
// Database plugin
pluginManager_.executePlugin("Database", {
{"query", "SELECT * FROM users WHERE active = 1"}
});
pluginManager_.executePlugin("Database", {
{"query", "INSERT INTO logs (message, timestamp) VALUES ('App started', NOW())"}
});
// Email plugin
pluginManager_.executePlugin("Email", {
{"to", "admin@example.com"},
{"subject", "Application Status"},
{"body", "Application is running normally"}
});
// Test plugin management
cout << "\n=== PLUGIN MANAGEMENT ===" << endl;
// Unload one plugin
pluginManager_.unloadPlugin("Database");
pluginManager_.printLoadedPlugins();
// Try to use unloaded plugin
pluginManager_.executePlugin("Database", {
{"query", "This should fail"}
});
// Reload plugin
pluginManager_.loadPlugin("Database");
pluginManager_.executePlugin("Database", {
{"query", "This should work again"}
});
cout << "\n=== APPLICATION ENDING ===" << endl;
// Plugins will be automatically shut down in destructor
}
};
int main() {
try {
Application app;
app.run();
} catch (const exception& e) {
cout << "❌ Application error: " << e.what() << endl;
return 1;
}
return 0;
}Ví dụ 3: State Machine với Strategy Pattern
#include <iostream>
#include <memory>
#include <string>
#include <map>
#include <functional>
#include <vector>
using namespace std;
// Forward declarations
class GameCharacter;
class GameState;
// State interface using modern C++
using StateAction = function<void(GameCharacter&)>;
using StateTransition = function<bool(GameCharacter&)>;
// State class
class GameState {
private:
string name_;
StateAction onEnter_;
StateAction onUpdate_;
StateAction onExit_;
map<string, pair<StateTransition, string>> transitions_;
public:
GameState(const string& name) : name_(name) {}
GameState& onEnter(StateAction action) {
onEnter_ = action;
return *this;
}
GameState& onUpdate(StateAction action) {
onUpdate_ = action;
return *this;
}
GameState& onExit(StateAction action) {
onExit_ = action;
return *this;
}
GameState& addTransition(const string& trigger,
StateTransition condition,
const string& targetState) {
transitions_[trigger] = make_pair(condition, targetState);
return *this;
}
const string& getName() const { return name_; }
void enter(GameCharacter& character) {
if (onEnter_) onEnter_(character);
}
void update(GameCharacter& character) {
if (onUpdate_) onUpdate_(character);
}
void exit(GameCharacter& character) {
if (onExit_) onExit_(character);
}
string checkTransitions(GameCharacter& character) {
for (const auto& pair : transitions_) {
if (pair.second.first(character)) {
return pair.second.second;
}
}
return "";
}
vector<string> getAvailableTransitions() const {
vector<string> triggers;
for (const auto& pair : transitions_) {
triggers.push_back(pair.first);
}
return triggers;
}
};
// Game Character class
class GameCharacter {
private:
string name_;
// Character stats
int health_;
int maxHealth_;
int energy_;
int maxEnergy_;
int experience_;
// State machine
map<string, unique_ptr<GameState>> states_;
string currentStateName_;
GameState* currentState_;
// Behaviors (Strategy pattern)
function<void(GameCharacter&)> attackBehavior_;
function<void(GameCharacter&)> moveBehavior_;
function<void(GameCharacter&)> defendBehavior_;
public:
GameCharacter(const string& name)
: name_(name), health_(100), maxHealth_(100),
energy_(100), maxEnergy_(100), experience_(0),
currentState_(nullptr) {
setupStates();
setupBehaviors();
setState("Idle");
cout << "⚔️ Character created: " << name_ << endl;
}
private:
void setupStates() {
// Idle state
auto idleState = make_unique<GameState>("Idle");
idleState->onEnter([](GameCharacter& c) {
cout << "😴 " << c.getName() << " is now idle" << endl;
})
.onUpdate([](GameCharacter& c) {
c.regenerateEnergy(2);
})
.addTransition("enemy_spotted", [](GameCharacter& c) {
return c.getEnergy() > 20;
}, "Combat")
.addTransition("low_health", [](GameCharacter& c) {
return c.getHealth() < 30;
}, "Fleeing")
.addTransition("explore", [](GameCharacter& c) {
return c.getEnergy() > 50;
}, "Exploring");
// Combat state
auto combatState = make_unique<GameState>("Combat");
combatState->onEnter([](GameCharacter& c) {
cout << "⚔️ " << c.getName() << " enters combat!" << endl;
})
.onUpdate([](GameCharacter& c) {
c.attack();
c.consumeEnergy(10);
// Simulate taking damage
if (rand() % 3 == 0) {
c.takeDamage(5);
}
})
.onExit([](GameCharacter& c) {
cout << "⚔️ " << c.getName() << " exits combat" << endl;
})
.addTransition("victory", [](GameCharacter& c) {
return rand() % 10 == 0; // Random victory
}, "Idle")
.addTransition("low_health", [](GameCharacter& c) {
return c.getHealth() < 20;
}, "Fleeing")
.addTransition("exhausted", [](GameCharacter& c) {
return c.getEnergy() < 10;
}, "Resting");
// Exploring state
auto exploringState = make_unique<GameState>("Exploring");
exploringState->onEnter([](GameCharacter& c) {
cout << "🗺️ " << c.getName() << " starts exploring" << endl;
})
.onUpdate([](GameCharacter& c) {
c.move();
c.consumeEnergy(5);
// Chance to find something
if (rand() % 5 == 0) {
c.gainExperience(10);
cout << "✨ " << c.getName() << " found something interesting!" << endl;
}
})
.addTransition("enemy_spotted", [](GameCharacter& c) {
return rand() % 8 == 0; // Random enemy encounter
}, "Combat")
.addTransition("tired", [](GameCharacter& c) {
return c.getEnergy() < 20;
}, "Resting")
.addTransition("bored", [](GameCharacter& c) {
return rand() % 15 == 0; // Random boredom
}, "Idle");
// Resting state
auto restingState = make_unique<GameState>("Resting");
restingState->onEnter([](GameCharacter& c) {
cout << "😪 " << c.getName() << " sits down to rest" << endl;
})
.onUpdate([](GameCharacter& c) {
c.regenerateHealth(3);
c.regenerateEnergy(8);
})
.addTransition("rested", [](GameCharacter& c) {
return c.getEnergy() > 80;
}, "Idle")
.addTransition("emergency", [](GameCharacter& c) {
return rand() % 20 == 0; // Random emergency
}, "Combat");
// Fleeing state
auto fleeingState = make_unique<GameState>("Fleeing");
fleeingState->onEnter([](GameCharacter& c) {
cout << "🏃 " << c.getName() << " runs away!" << endl;
})
.onUpdate([](GameCharacter& c) {
c.move(); // Fast movement
c.consumeEnergy(15);
})
.addTransition("safe", [](GameCharacter& c) {
return rand() % 5 == 0; // Escape chance
}, "Resting")
.addTransition("cornered", [](GameCharacter& c) {
return c.getEnergy() < 10;
}, "Combat");
// Store states
states_["Idle"] = move(idleState);
states_["Combat"] = move(combatState);
states_["Exploring"] = move(exploringState);
states_["Resting"] = move(restingState);
states_["Fleeing"] = move(fleeingState);
}
void setupBehaviors() {
// Attack behaviors
attackBehavior_ = [](GameCharacter& c) {
cout << "⚔️ " << c.getName() << " swings sword!" << endl;
};
// Movement behaviors
moveBehavior_ = [](GameCharacter& c) {
cout << "🚶 " << c.getName() << " moves forward" << endl;
};
// Defense behaviors
defendBehavior_ = [](GameCharacter& c) {
cout << "🛡️ " << c.getName() << " raises shield!" << endl;
};
}
public:
// State management
void setState(const string& stateName) {
auto it = states_.find(stateName);
if (it == states_.end()) {
cout << "❌ Unknown state: " << stateName << endl;
return;
}
if (currentState_) {
currentState_->exit(*this);
}
currentStateName_ = stateName;
currentState_ = it->second.get();
currentState_->enter(*this);
}
void update() {
if (currentState_) {
currentState_->update(*this);
// Check for state transitions
string newState = currentState_->checkTransitions(*this);
if (!newState.empty() && newState != currentStateName_) {
setState(newState);
}
}
}
// Behavior methods (using strategies)
void attack() {
if (attackBehavior_) attackBehavior_(*this);
}
void move() {
if (moveBehavior_) moveBehavior_(*this);
}
void defend() {
if (defendBehavior_) defendBehavior_(*this);
}
// Change behaviors at runtime
void setAttackBehavior(function<void(GameCharacter&)> behavior) {
attackBehavior_ = behavior;
}
void setMoveBehavior(function<void(GameCharacter&)> behavior) {
moveBehavior_ = behavior;
}
void setDefendBehavior(function<void(GameCharacter&)> behavior) {
defendBehavior_ = behavior;
}
// Character actions
void takeDamage(int amount) {
health_ = max(0, health_ - amount);
cout << "💥 " << name_ << " takes " << amount << " damage (Health: " << health_ << ")" << endl;
}
void regenerateHealth(int amount) {
health_ = min(maxHealth_, health_ + amount);
cout << "❤️ " << name_ << " heals " << amount << " (Health: " << health_ << ")" << endl;
}
void consumeEnergy(int amount) {
energy_ = max(0, energy_ - amount);
}
void regenerateEnergy(int amount) {
energy_ = min(maxEnergy_, energy_ + amount);
}
void gainExperience(int amount) {
experience_ += amount;
cout << "✨ " << name_ << " gains " << amount << " XP (Total: " << experience_ << ")" << endl;
}
// Getters
const string& getName() const { return name_; }
int getHealth() const { return health_; }
int getMaxHealth() const { return maxHealth_; }
int getEnergy() const { return energy_; }
int getMaxEnergy() const { return maxEnergy_; }
int getExperience() const { return experience_; }
const string& getCurrentState() const { return currentStateName_; }
void printStatus() const {
cout << "👤 " << name_ << " [" << currentStateName_ << "]" << endl;
cout << " Health: " << health_ << "/" << maxHealth_ << endl;
cout << " Energy: " << energy_ << "/" << maxEnergy_ << endl;
cout << " Experience: " << experience_ << endl;
if (currentState_) {
auto transitions = currentState_->getAvailableTransitions();
if (!transitions.empty()) {
cout << " Available transitions: ";
for (const auto& t : transitions) {
cout << t << " ";
}
cout << endl;
}
}
}
};
// Game simulation
class GameWorld {
private:
vector<unique_ptr<GameCharacter>> characters_;
int turnCount_;
public:
GameWorld() : turnCount_(0) {
cout << "🌍 Game world created" << endl;
}
void addCharacter(const string& name) {
auto character = make_unique<GameCharacter>(name);
// Set different behaviors for variety
if (characters_.size() % 2 == 0) {
// Aggressive character
character->setAttackBehavior([](GameCharacter& c) {
cout << "⚔️ " << c.getName() << " unleashes a fierce attack!" << endl;
});
} else {
// Defensive character
character->setAttackBehavior([](GameCharacter& c) {
cout << "⚔️ " << c.getName() << " carefully strikes" << endl;
});
}
characters_.push_back(move(character));
}
void simulate(int turns) {
cout << "\n🎮 Starting game simulation for " << turns << " turns..." << endl;
for (int turn = 1; turn <= turns; turn++) {
cout << "\n--- Turn " << turn << " ---" << endl;
for (auto& character : characters_) {
character->update();
}
// Print status every 5 turns
if (turn % 5 == 0) {
cout << "\n📊 CHARACTER STATUS:" << endl;
for (const auto& character : characters_) {
character->printStatus();
cout << endl;
}
}
// Add some pause for readability
if (turn % 10 == 0) {
cout << "\n⏸️ --- Checkpoint ---" << endl;
}
}
cout << "\n🎮 Simulation completed!" << endl;
}
void printFinalStats() {
cout << "\n📊 FINAL CHARACTER STATS:" << endl;
for (const auto& character : characters_) {
character->printStatus();
cout << endl;
}
}
};
int main() {
cout << "=== STATE MACHINE WITH STRATEGY PATTERN DEMO ===" << endl;
srand(static_cast<unsigned>(time(nullptr))); // For random events
GameWorld world;
// Add characters
world.addCharacter("Arthur");
world.addCharacter("Merlin");
world.addCharacter("Lancelot");
// Run simulation
world.simulate(30);
world.printFinalStats();
return 0;
}🏋️ Thực hành
Bài tập 1: Command Pattern với Undo/Redo
Implement command pattern với undo/redo functionality:
- Command interface với execute/undo methods
- Command history stack
- Macro commands (composite commands)
- Modern C++ với smart pointers
Bài tập 2: Decorator Pattern cho Graphics System
Tạo graphics system với decorator pattern:
- Base shape interface
- Concrete shapes (circle, rectangle)
- Decorators (border, shadow, animation)
- Chain multiple decorators
Bài tập 3: Template Method Pattern
Implement data processing pipeline:
- Abstract algorithm template
- Concrete implementations cho different data types
- Hook methods cho customization
- Modern C++ templates và concepts
Bài tập 4: Visitor Pattern cho AST
Tạo Abstract Syntax Tree với visitor pattern:
- AST nodes (expressions, statements)
- Visitors (evaluator, printer, optimizer)
- Double dispatch mechanism
- Type safety với variants
Lời giải chi tiết
Bài tập 1 - Command Pattern:
#include <iostream>
#include <memory>
#include <stack>
#include <vector>
#include <string>
using namespace std;
// Command interface
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
virtual string getDescription() const = 0;
virtual bool canUndo() const { return true; }
};
// Receiver - Text Editor
class TextEditor {
private:
string content_;
public:
void insertText(size_t position, const string& text) {
if (position <= content_.size()) {
content_.insert(position, text);
cout << "📝 Inserted: \"" << text << "\" at position " << position << endl;
}
}
string deleteText(size_t position, size_t length) {
if (position < content_.size()) {
string deleted = content_.substr(position, length);
content_.erase(position, length);
cout << "🗑️ Deleted: \"" << deleted << "\" at position " << position << endl;
return deleted;
}
return "";
}
void replaceText(size_t position, size_t length, const string& newText) {
if (position < content_.size()) {
content_.erase(position, length);
content_.insert(position, newText);
cout << "🔄 Replaced text at position " << position << endl;
}
}
const string& getContent() const { return content_; }
void printContent() const {
cout << "📄 Content: \"" << content_ << "\"" << endl;
}
};
// Concrete Commands
class InsertCommand : public Command {
private:
TextEditor& editor_;
size_t position_;
string text_;
public:
InsertCommand(TextEditor& editor, size_t pos, const string& text)
: editor_(editor), position_(pos), text_(text) {}
void execute() override {
editor_.insertText(position_, text_);
}
void undo() override {
editor_.deleteText(position_, text_.length());
}
string getDescription() const override {
return "Insert \"" + text_ + "\" at " + to_string(position_);
}
};
class DeleteCommand : public Command {
private:
TextEditor& editor_;
size_t position_;
size_t length_;
string deletedText_;
public:
DeleteCommand(TextEditor& editor, size_t pos, size_t len)
: editor_(editor), position_(pos), length_(len) {}
void execute() override {
deletedText_ = editor_.deleteText(position_, length_);
}
void undo() override {
editor_.insertText(position_, deletedText_);
}
string getDescription() const override {
return "Delete " + to_string(length_) + " chars at " + to_string(position_);
}
};
// Macro Command (Composite)
class MacroCommand : public Command {
private:
vector<unique_ptr<Command>> commands_;
string description_;
public:
MacroCommand(const string& desc) : description_(desc) {}
void addCommand(unique_ptr<Command> command) {
commands_.push_back(move(command));
}
void execute() override {
cout << "🎯 Executing macro: " << description_ << endl;
for (auto& cmd : commands_) {
cmd->execute();
}
}
void undo() override {
cout << "↩️ Undoing macro: " << description_ << endl;
// Undo in reverse order
for (auto it = commands_.rbegin(); it != commands_.rend(); ++it) {
(*it)->undo();
}
}
string getDescription() const override {
return "Macro: " + description_;
}
};
// Command Manager with undo/redo
class CommandManager {
private:
stack<unique_ptr<Command>> undoStack_;
stack<unique_ptr<Command>> redoStack_;
size_t maxHistorySize_;
public:
CommandManager(size_t maxHistory = 100) : maxHistorySize_(maxHistory) {}
void executeCommand(unique_ptr<Command> command) {
command->execute();
// Clear redo stack when new command is executed
while (!redoStack_.empty()) {
redoStack_.pop();
}
undoStack_.push(move(command));
// Limit history size
if (undoStack_.size() > maxHistorySize_) {
// Remove oldest commands (complex implementation needed)
}
}
bool undo() {
if (undoStack_.empty()) {
cout << "❌ Nothing to undo" << endl;
return false;
}
auto command = move(undoStack_.top());
undoStack_.pop();
cout << "↩️ Undoing: " << command->getDescription() << endl;
command->undo();
redoStack_.push(move(command));
return true;
}
bool redo() {
if (redoStack_.empty()) {
cout << "❌ Nothing to redo" << endl;
return false;
}
auto command = move(redoStack_.top());
redoStack_.pop();
cout << "↪️ Redoing: " << command->getDescription() << endl;
command->execute();
undoStack_.push(move(command));
return true;
}
void printHistory() const {
cout << "\n📚 COMMAND HISTORY:" << endl;
cout << "Undo stack size: " << undoStack_.size() << endl;
cout << "Redo stack size: " << redoStack_.size() << endl;
}
};📋 Tóm tắt
Các điểm quan trọng
Design Patterns Benefits:
- Reusability: Tái sử dụng proven solutions
- Communication: Common vocabulary giữa developers
- Maintainability: Code structure rõ ràng và consistent
- Flexibility: Easy to extend và modify
Modern C++ Improvements:
- Smart Pointers: Thay thế raw pointers cho memory safety
- RAII: Automatic resource management
- Lambda Functions: Thay thế function objects trong nhiều cases
- std::function: Type-erased callable objects
- Template Metaprogramming: Generic và flexible patterns
Pattern Categories:
- Creational: Singleton, Factory, Builder
- Structural: Adapter, Decorator, Facade
- Behavioral: Observer, Strategy, Command
Best Practices
Choosing Patterns:
- Đừng force patterns nếu không cần thiết
- Understand problem trước khi apply pattern
- Consider simpler solutions trước
- Use modern C++ features để simplify implementation
Implementation Guidelines:
- Prefer composition over inheritance
- Use smart pointers cho memory management
- Apply RAII principle
- Make interfaces minimal và focused
- Consider thread safety nếu cần
Common Pitfalls:
- Over-engineering với unnecessary patterns
- Misunderstanding pattern intent
- Ignoring performance implications
- Not adapting patterns to specific context
Chuẩn bị cho bài tiếp theo
Đây là bài cuối của phần Advanced. Tiếp theo sẽ là phần Algorithms với:
- Complexity Analysis: Big-O notation và performance analysis
- Sorting Algorithms: Bubble, insertion, quick, merge sort
- Searching Algorithms: Linear, binary search
- Advanced Topics: Dynamic programming, graph algorithms
- Modern C++ STL: Using standard algorithms efficiently
Design Patterns là tools quan trọng cho software architecture. Hãy practice để biết khi nào và cách nào apply chúng effectively!