diff --git a/src/content/design-pattern/code/abstract-factory/solution.cpp b/src/content/design-pattern/code/abstract-factory/solution.cpp new file mode 100644 index 0000000..5f89ae4 --- /dev/null +++ b/src/content/design-pattern/code/abstract-factory/solution.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +/** + * Abstract Factory Pattern - Cross-Platform Graphics Framework + * Creates families of related objects for different rendering engines + */ + +// Abstract products +class Renderer { +public: + virtual ~Renderer() = default; + virtual void renderShape(const std::string& shape) = 0; + virtual void setColor(const std::string& color) = 0; +}; + +class Window { +public: + virtual ~Window() = default; + virtual void create(int width, int height) = 0; + virtual void show() = 0; + virtual void close() = 0; +}; + +// Concrete OpenGL products +class OpenGLRenderer : public Renderer { +private: + std::string currentColor = "white"; + +public: + void renderShape(const std::string& shape) override { + std::cout << "OpenGL: Rendering " << shape << " with hardware acceleration" << std::endl; + std::cout << "OpenGL: Using vertex shaders and " << currentColor << " color" << std::endl; + } + + void setColor(const std::string& color) override { + currentColor = color; + std::cout << "OpenGL: Setting color to " << color << " using RGB values" << std::endl; + } +}; + +class OpenGLWindow : public Window { +private: + int width, height; + bool isOpen = false; + +public: + void create(int w, int h) override { + width = w; + height = h; + std::cout << "OpenGL: Creating window " << width << "x" << height + << " with double buffering" << std::endl; + } + + void show() override { + isOpen = true; + std::cout << "OpenGL: Showing window with hardware-accelerated context" << std::endl; + } + + void close() override { + isOpen = false; + std::cout << "OpenGL: Closing window and cleaning up OpenGL context" << std::endl; + } +}; + +// Concrete DirectX products +class DirectXRenderer : public Renderer { +private: + std::string currentColor = "white"; + +public: + void renderShape(const std::string& shape) override { + std::cout << "DirectX: Rendering " << shape << " with Direct3D pipeline" << std::endl; + std::cout << "DirectX: Using HLSL shaders and " << currentColor << " color" << std::endl; + } + + void setColor(const std::string& color) override { + currentColor = color; + std::cout << "DirectX: Setting color to " << color << " using DXGI format" << std::endl; + } +}; + +class DirectXWindow : public Window { +private: + int width, height; + bool isOpen = false; + +public: + void create(int w, int h) override { + width = w; + height = h; + std::cout << "DirectX: Creating window " << width << "x" << height + << " with DXGI swap chain" << std::endl; + } + + void show() override { + isOpen = true; + std::cout << "DirectX: Showing window with DirectX 12 context" << std::endl; + } + + void close() override { + isOpen = false; + std::cout << "DirectX: Closing window and releasing DirectX resources" << std::endl; + } +}; + +// Abstract Factory +class GraphicsFactory { +public: + virtual ~GraphicsFactory() = default; + virtual std::unique_ptr createRenderer() = 0; + virtual std::unique_ptr createWindow() = 0; +}; + +// Concrete Factories +class OpenGLFactory : public GraphicsFactory { +public: + std::unique_ptr createRenderer() override { + return std::make_unique(); + } + + std::unique_ptr createWindow() override { + return std::make_unique(); + } +}; + +class DirectXFactory : public GraphicsFactory { +public: + std::unique_ptr createRenderer() override { + return std::make_unique(); + } + + std::unique_ptr createWindow() override { + return std::make_unique(); + } +}; + +// Client application +class GraphicsApplication { +private: + std::unique_ptr renderer; + std::unique_ptr window; + +public: + GraphicsApplication(std::unique_ptr factory) { + renderer = factory->createRenderer(); + window = factory->createWindow(); + } + + void initialize() { + std::cout << "Initializing graphics application..." << std::endl; + window->create(800, 600); + window->show(); + } + + void render() { + std::cout << "\n--- Rendering Scene ---" << std::endl; + + renderer->setColor("blue"); + renderer->renderShape("triangle"); + + renderer->setColor("red"); + renderer->renderShape("rectangle"); + + renderer->setColor("green"); + renderer->renderShape("circle"); + } + + void cleanup() { + std::cout << "\nCleaning up graphics resources..." << std::endl; + window->close(); + } +}; + +int main() { + std::cout << "=== Abstract Factory Pattern - Graphics Framework ===\n" << std::endl; + + // Platform detection (simplified) + std::string platform; + +#ifdef _WIN32 + platform = "Windows"; +#else + platform = "Linux"; +#endif + + std::cout << "Detected platform: " << platform << std::endl; + + std::unique_ptr factory; + + if (platform == "Windows") { + std::cout << "Creating DirectX Graphics Factory..." << std::endl; + factory = std::make_unique(); + } else { + std::cout << "Creating OpenGL Graphics Factory..." << std::endl; + factory = std::make_unique(); + } + + // Create and run application + GraphicsApplication app(std::move(factory)); + + app.initialize(); + app.render(); + app.cleanup(); + + std::cout << "\n--- Testing Both Graphics APIs ---" << std::endl; + + // Demo both graphics systems + std::cout << "\nOpenGL Graphics System:" << std::endl; + auto openglFactory = std::make_unique(); + GraphicsApplication openglApp(std::move(openglFactory)); + openglApp.initialize(); + openglApp.render(); + openglApp.cleanup(); + + std::cout << "\nDirectX Graphics System:" << std::endl; + auto directxFactory = std::make_unique(); + GraphicsApplication directxApp(std::move(directxFactory)); + directxApp.initialize(); + directxApp.render(); + directxApp.cleanup(); + + return 0; +} diff --git a/src/content/design-pattern/code/abstract-factory/solution.java b/src/content/design-pattern/code/abstract-factory/solution.java new file mode 100644 index 0000000..9ddd0d5 --- /dev/null +++ b/src/content/design-pattern/code/abstract-factory/solution.java @@ -0,0 +1,160 @@ +/** + * Abstract Factory Pattern - GUI Framework Example + * Creates families of related objects (Windows/Mac UI components) without specifying their concrete classes + */ + +// Abstract products +interface Button { + void click(); + void render(); +} + +interface Checkbox { + void check(); + void render(); +} + +// Concrete Windows products +class WindowsButton implements Button { + @Override + public void click() { + System.out.println("Windows button clicked - Opening file dialog"); + } + + @Override + public void render() { + System.out.println("Rendering Windows-style button with system theme"); + } +} + +class WindowsCheckbox implements Checkbox { + @Override + public void check() { + System.out.println("Windows checkbox toggled with system sound"); + } + + @Override + public void render() { + System.out.println("Rendering Windows-style checkbox with blue checkmark"); + } +} + +// Concrete Mac products +class MacButton implements Button { + @Override + public void click() { + System.out.println("Mac button clicked - Smooth animation effect"); + } + + @Override + public void render() { + System.out.println("Rendering Mac-style button with rounded corners"); + } +} + +class MacCheckbox implements Checkbox { + @Override + public void check() { + System.out.println("Mac checkbox toggled with subtle haptic feedback"); + } + + @Override + public void render() { + System.out.println("Rendering Mac-style checkbox with gradient effect"); + } +} + +// Abstract Factory +interface GUIFactory { + Button createButton(); + Checkbox createCheckbox(); +} + +// Concrete Factories +class WindowsFactory implements GUIFactory { + @Override + public Button createButton() { + return new WindowsButton(); + } + + @Override + public Checkbox createCheckbox() { + return new WindowsCheckbox(); + } +} + +class MacFactory implements GUIFactory { + @Override + public Button createButton() { + return new MacButton(); + } + + @Override + public Checkbox createCheckbox() { + return new MacCheckbox(); + } +} + +// Client application +class Application { + private Button button; + private Checkbox checkbox; + + public Application(GUIFactory factory) { + this.button = factory.createButton(); + this.checkbox = factory.createCheckbox(); + } + + public void renderUI() { + button.render(); + checkbox.render(); + } + + public void handleInteraction() { + button.click(); + checkbox.check(); + } +} + +public class AbstractFactoryDemo { + public static void main(String[] args) { + System.out.println("=== Abstract Factory Pattern Demo ===\n"); + + // Determine OS and create appropriate factory + String os = System.getProperty("os.name").toLowerCase(); + GUIFactory factory; + + if (os.contains("win")) { + System.out.println("Creating Windows GUI Factory..."); + factory = new WindowsFactory(); + } else if (os.contains("mac")) { + System.out.println("Creating Mac GUI Factory..."); + factory = new MacFactory(); + } else { + System.out.println("Defaulting to Windows GUI Factory..."); + factory = new WindowsFactory(); + } + + // Create application with platform-specific components + Application app = new Application(factory); + + System.out.println("\n--- Rendering UI Components ---"); + app.renderUI(); + + System.out.println("\n--- User Interactions ---"); + app.handleInteraction(); + + System.out.println("\n--- Testing Both Factories ---"); + + // Demo both factories + System.out.println("\nWindows Factory:"); + Application windowsApp = new Application(new WindowsFactory()); + windowsApp.renderUI(); + windowsApp.handleInteraction(); + + System.out.println("\nMac Factory:"); + Application macApp = new Application(new MacFactory()); + macApp.renderUI(); + macApp.handleInteraction(); + } +} diff --git a/src/content/design-pattern/code/abstract-factory/solution.py b/src/content/design-pattern/code/abstract-factory/solution.py new file mode 100644 index 0000000..cbb1fb2 --- /dev/null +++ b/src/content/design-pattern/code/abstract-factory/solution.py @@ -0,0 +1,168 @@ +""" +Abstract Factory Pattern - Database Factory Example +Creates families of related objects (database connections and queries) without specifying concrete classes +""" + +from abc import ABC, abstractmethod +import platform + +# Abstract products +class DatabaseConnection(ABC): + @abstractmethod + def connect(self): + pass + + @abstractmethod + def disconnect(self): + pass + +class QueryBuilder(ABC): + @abstractmethod + def select(self, table, columns="*"): + pass + + @abstractmethod + def insert(self, table, data): + pass + +# Concrete MySQL products +class MySQLConnection(DatabaseConnection): + def __init__(self): + self.host = "localhost" + self.port = 3306 + + def connect(self): + print(f"Connected to MySQL database at {self.host}:{self.port}") + print("Using MySQL-specific connection pooling and SSL encryption") + + def disconnect(self): + print("Disconnected from MySQL database with proper cleanup") + +class MySQLQueryBuilder(QueryBuilder): + def select(self, table, columns="*"): + query = f"SELECT {columns} FROM `{table}`" + print(f"MySQL Query: {query} (with backticks for table names)") + return query + + def insert(self, table, data): + columns = ", ".join([f"`{col}`" for col in data.keys()]) + values = ", ".join([f"'{val}'" for val in data.values()]) + query = f"INSERT INTO `{table}` ({columns}) VALUES ({values})" + print(f"MySQL Query: {query} (with backticks and MySQL syntax)") + return query + +# Concrete PostgreSQL products +class PostgreSQLConnection(DatabaseConnection): + def __init__(self): + self.host = "localhost" + self.port = 5432 + + def connect(self): + print(f"Connected to PostgreSQL database at {self.host}:{self.port}") + print("Using PostgreSQL-specific connection with advanced indexing") + + def disconnect(self): + print("Disconnected from PostgreSQL database with transaction rollback") + +class PostgreSQLQueryBuilder(QueryBuilder): + def select(self, table, columns="*"): + query = f"SELECT {columns} FROM \"{table}\"" + print(f"PostgreSQL Query: {query} (with double quotes for identifiers)") + return query + + def insert(self, table, data): + columns = ", ".join([f'"{col}"' for col in data.keys()]) + values = ", ".join([f"'{val}'" for val in data.values()]) + query = f"INSERT INTO \"{table}\" ({columns}) VALUES ({values})" + print(f"PostgreSQL Query: {query} (with double quotes and PostgreSQL syntax)") + return query + +# Abstract Factory +class DatabaseFactory(ABC): + @abstractmethod + def create_connection(self): + pass + + @abstractmethod + def create_query_builder(self): + pass + +# Concrete Factories +class MySQLFactory(DatabaseFactory): + def create_connection(self): + return MySQLConnection() + + def create_query_builder(self): + return MySQLQueryBuilder() + +class PostgreSQLFactory(DatabaseFactory): + def create_connection(self): + return PostgreSQLConnection() + + def create_query_builder(self): + return PostgreSQLQueryBuilder() + +# Client application +class DatabaseManager: + def __init__(self, factory: DatabaseFactory): + self.connection = factory.create_connection() + self.query_builder = factory.create_query_builder() + + def setup_database(self): + print("Setting up database connection...") + self.connection.connect() + + def perform_operations(self): + print("\n--- Database Operations ---") + + # Select operation + self.query_builder.select("users", "name, email") + + # Insert operation + user_data = {"name": "John Doe", "email": "john@example.com", "age": "30"} + self.query_builder.insert("users", user_data) + + def cleanup(self): + print("\nCleaning up database connection...") + self.connection.disconnect() + +def main(): + print("=== Abstract Factory Pattern - Database Demo ===\n") + + # Configuration-based factory selection + database_type = "postgresql" # Could come from config file + + print(f"Selected database type: {database_type}") + + if database_type.lower() == "mysql": + factory = MySQLFactory() + print("Using MySQL Database Factory") + elif database_type.lower() == "postgresql": + factory = PostgreSQLFactory() + print("Using PostgreSQL Database Factory") + else: + print("Unknown database type, defaulting to MySQL") + factory = MySQLFactory() + + # Create database manager with appropriate factory + db_manager = DatabaseManager(factory) + + try: + db_manager.setup_database() + db_manager.perform_operations() + finally: + db_manager.cleanup() + + print("\n--- Testing Both Database Types ---") + + # Demo both database types + for db_type, factory_class in [("MySQL", MySQLFactory), ("PostgreSQL", PostgreSQLFactory)]: + print(f"\n{db_type} Database:") + factory = factory_class() + manager = DatabaseManager(factory) + manager.setup_database() + manager.perform_operations() + manager.cleanup() + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/builder/solution.cpp b/src/content/design-pattern/code/builder/solution.cpp new file mode 100644 index 0000000..a0dd41c --- /dev/null +++ b/src/content/design-pattern/code/builder/solution.cpp @@ -0,0 +1,483 @@ +#include +#include +#include +#include +#include +#include + +/** + * Builder Pattern - SQL Query Builder Example + * Constructs complex SQL queries with fluent interface and validation + */ + +class SqlQuery { +private: + std::string queryType; + std::string tableName; + std::vector selectColumns; + std::map insertValues; + std::map updateValues; + std::vector whereConditions; + std::vector orderByColumns; + std::vector groupByColumns; + std::string havingCondition; + int limitValue; + int offsetValue; + std::vector joinClauses; + +public: + SqlQuery(const std::string& type, const std::string& table) + : queryType(type), tableName(table), limitValue(-1), offsetValue(-1) {} + + // Getters for builder access + std::string getQueryType() const { return queryType; } + std::string getTableName() const { return tableName; } + const std::vector& getSelectColumns() const { return selectColumns; } + const std::map& getInsertValues() const { return insertValues; } + const std::map& getUpdateValues() const { return updateValues; } + const std::vector& getWhereConditions() const { return whereConditions; } + const std::vector& getOrderByColumns() const { return orderByColumns; } + const std::vector& getGroupByColumns() const { return groupByColumns; } + const std::string& getHavingCondition() const { return havingCondition; } + int getLimitValue() const { return limitValue; } + int getOffsetValue() const { return offsetValue; } + const std::vector& getJoinClauses() const { return joinClauses; } + + // Setters for builder + void setSelectColumns(const std::vector& columns) { selectColumns = columns; } + void setInsertValues(const std::map& values) { insertValues = values; } + void setUpdateValues(const std::map& values) { updateValues = values; } + void setWhereConditions(const std::vector& conditions) { whereConditions = conditions; } + void setOrderByColumns(const std::vector& columns) { orderByColumns = columns; } + void setGroupByColumns(const std::vector& columns) { groupByColumns = columns; } + void setHavingCondition(const std::string& condition) { havingCondition = condition; } + void setLimitValue(int limit) { limitValue = limit; } + void setOffsetValue(int offset) { offsetValue = offset; } + void setJoinClauses(const std::vector& joins) { joinClauses = joins; } + + std::string toSql() const { + std::string query; + + if (queryType == "SELECT") { + query = "SELECT "; + + if (selectColumns.empty()) { + query += "*"; + } else { + for (size_t i = 0; i < selectColumns.size(); ++i) { + query += selectColumns[i]; + if (i < selectColumns.size() - 1) query += ", "; + } + } + + query += " FROM " + tableName; + + // Add joins + for (const auto& join : joinClauses) { + query += " " + join; + } + + } else if (queryType == "INSERT") { + query = "INSERT INTO " + tableName + " ("; + + size_t i = 0; + for (const auto& pair : insertValues) { + query += pair.first; + if (++i < insertValues.size()) query += ", "; + } + + query += ") VALUES ("; + + i = 0; + for (const auto& pair : insertValues) { + query += "'" + pair.second + "'"; + if (++i < insertValues.size()) query += ", "; + } + query += ")"; + + } else if (queryType == "UPDATE") { + query = "UPDATE " + tableName + " SET "; + + size_t i = 0; + for (const auto& pair : updateValues) { + query += pair.first + " = '" + pair.second + "'"; + if (++i < updateValues.size()) query += ", "; + } + + } else if (queryType == "DELETE") { + query = "DELETE FROM " + tableName; + } + + // Add WHERE clause + if (!whereConditions.empty()) { + query += " WHERE "; + for (size_t i = 0; i < whereConditions.size(); ++i) { + query += whereConditions[i]; + if (i < whereConditions.size() - 1) query += " AND "; + } + } + + // Add GROUP BY clause + if (!groupByColumns.empty()) { + query += " GROUP BY "; + for (size_t i = 0; i < groupByColumns.size(); ++i) { + query += groupByColumns[i]; + if (i < groupByColumns.size() - 1) query += ", "; + } + } + + // Add HAVING clause + if (!havingCondition.empty()) { + query += " HAVING " + havingCondition; + } + + // Add ORDER BY clause + if (!orderByColumns.empty()) { + query += " ORDER BY "; + for (size_t i = 0; i < orderByColumns.size(); ++i) { + query += orderByColumns[i]; + if (i < orderByColumns.size() - 1) query += ", "; + } + } + + // Add LIMIT clause + if (limitValue > 0) { + query += " LIMIT " + std::to_string(limitValue); + } + + // Add OFFSET clause + if (offsetValue > 0) { + query += " OFFSET " + std::to_string(offsetValue); + } + + return query + ";"; + } + + void execute() const { + std::cout << "🗄️ Executing SQL Query:" << std::endl; + std::cout << "📝 " << toSql() << std::endl; + std::cout << "⏱️ Query type: " << queryType << " on table: " << tableName << std::endl; + std::cout << "✅ Query executed successfully!" << std::endl; + } + + void explain() const { + std::cout << "SQL Query Analysis:" << std::endl; + std::cout << "├─ Type: " << queryType << std::endl; + std::cout << "├─ Table: " << tableName << std::endl; + + if (!selectColumns.empty()) { + std::cout << "├─ Columns: " << selectColumns.size() << " selected" << std::endl; + } + + if (!whereConditions.empty()) { + std::cout << "├─ WHERE conditions: " << whereConditions.size() << std::endl; + } + + if (!joinClauses.empty()) { + std::cout << "├─ JOINs: " << joinClauses.size() << std::endl; + } + + if (!orderByColumns.empty()) { + std::cout << "├─ ORDER BY: " << orderByColumns.size() << " columns" << std::endl; + } + + if (limitValue > 0) { + std::cout << "├─ LIMIT: " << limitValue << " rows" << std::endl; + } + + std::cout << "└─ Generated SQL: " << toSql().length() << " characters" << std::endl; + } +}; + +class SqlQueryBuilder { +private: + std::unique_ptr query; + +public: + SqlQueryBuilder(const std::string& queryType, const std::string& tableName) { + query = std::make_unique(queryType, tableName); + } + + // SELECT specific methods + SqlQueryBuilder& select(const std::vector& columns) { + if (query->getQueryType() != "SELECT") { + throw std::invalid_argument("select() can only be used with SELECT queries"); + } + query->setSelectColumns(columns); + return *this; + } + + SqlQueryBuilder& select(const std::string& column) { + return select(std::vector{column}); + } + + // INSERT specific methods + SqlQueryBuilder& values(const std::map& values) { + if (query->getQueryType() != "INSERT") { + throw std::invalid_argument("values() can only be used with INSERT queries"); + } + query->setInsertValues(values); + return *this; + } + + SqlQueryBuilder& value(const std::string& column, const std::string& value) { + auto currentValues = query->getInsertValues(); + currentValues[column] = value; + return values(currentValues); + } + + // UPDATE specific methods + SqlQueryBuilder& set(const std::map& values) { + if (query->getQueryType() != "UPDATE") { + throw std::invalid_argument("set() can only be used with UPDATE queries"); + } + query->setUpdateValues(values); + return *this; + } + + SqlQueryBuilder& set(const std::string& column, const std::string& value) { + auto currentValues = query->getUpdateValues(); + currentValues[column] = value; + return set(currentValues); + } + + // Common methods for all query types + SqlQueryBuilder& where(const std::string& condition) { + auto conditions = query->getWhereConditions(); + conditions.push_back(condition); + query->setWhereConditions(conditions); + return *this; + } + + SqlQueryBuilder& whereEquals(const std::string& column, const std::string& value) { + return where(column + " = '" + value + "'"); + } + + SqlQueryBuilder& whereLike(const std::string& column, const std::string& pattern) { + return where(column + " LIKE '" + pattern + "'"); + } + + SqlQueryBuilder& whereIn(const std::string& column, const std::vector& values) { + std::string condition = column + " IN ("; + for (size_t i = 0; i < values.size(); ++i) { + condition += "'" + values[i] + "'"; + if (i < values.size() - 1) condition += ", "; + } + condition += ")"; + return where(condition); + } + + // JOIN methods + SqlQueryBuilder& innerJoin(const std::string& table, const std::string& condition) { + auto joins = query->getJoinClauses(); + joins.push_back("INNER JOIN " + table + " ON " + condition); + query->setJoinClauses(joins); + return *this; + } + + SqlQueryBuilder& leftJoin(const std::string& table, const std::string& condition) { + auto joins = query->getJoinClauses(); + joins.push_back("LEFT JOIN " + table + " ON " + condition); + query->setJoinClauses(joins); + return *this; + } + + // ORDER BY methods + SqlQueryBuilder& orderBy(const std::string& column) { + auto columns = query->getOrderByColumns(); + columns.push_back(column); + query->setOrderByColumns(columns); + return *this; + } + + SqlQueryBuilder& orderBy(const std::string& column, const std::string& direction) { + return orderBy(column + " " + direction); + } + + // GROUP BY methods + SqlQueryBuilder& groupBy(const std::string& column) { + auto columns = query->getGroupByColumns(); + columns.push_back(column); + query->setGroupByColumns(columns); + return *this; + } + + SqlQueryBuilder& having(const std::string& condition) { + query->setHavingCondition(condition); + return *this; + } + + // LIMIT and OFFSET + SqlQueryBuilder& limit(int count) { + if (count <= 0) { + throw std::invalid_argument("LIMIT must be positive"); + } + query->setLimitValue(count); + return *this; + } + + SqlQueryBuilder& offset(int count) { + if (count < 0) { + throw std::invalid_argument("OFFSET cannot be negative"); + } + query->setOffsetValue(count); + return *this; + } + + // Build method + std::unique_ptr build() { + // Validation + if (query->getQueryType() == "INSERT" && query->getInsertValues().empty()) { + throw std::invalid_argument("INSERT query must have values"); + } + + if (query->getQueryType() == "UPDATE" && query->getUpdateValues().empty()) { + throw std::invalid_argument("UPDATE query must have SET values"); + } + + if ((query->getQueryType() == "UPDATE" || query->getQueryType() == "DELETE") + && query->getWhereConditions().empty()) { + std::cout << "⚠️ Warning: " << query->getQueryType() + << " query without WHERE clause affects all rows!" << std::endl; + } + + return std::move(query); + } +}; + +// Director class for common query patterns +class SqlQueryDirector { +public: + static std::unique_ptr selectAllFromTable(const std::string& tableName) { + return SqlQueryBuilder("SELECT", tableName).build(); + } + + static std::unique_ptr selectUserById(int userId) { + return SqlQueryBuilder("SELECT", "users") + .select({"id", "username", "email", "created_at"}) + .whereEquals("id", std::to_string(userId)) + .build(); + } + + static std::unique_ptr paginatedSelect(const std::string& tableName, + int pageSize, int pageNumber) { + return SqlQueryBuilder("SELECT", tableName) + .orderBy("id") + .limit(pageSize) + .offset(pageSize * (pageNumber - 1)) + .build(); + } + + static std::unique_ptr createUser(const std::string& username, + const std::string& email) { + return SqlQueryBuilder("INSERT", "users") + .value("username", username) + .value("email", email) + .value("created_at", "NOW()") + .build(); + } +}; + +int main() { + std::cout << "=== Builder Pattern Demo - SQL Query Builder ===\n" << std::endl; + + try { + // Example 1: Complex SELECT query + std::cout << "1. Complex SELECT Query with JOINs:" << std::endl; + auto complexQuery = SqlQueryBuilder("SELECT", "users") + .select({"u.username", "u.email", "p.title as profile_title", "COUNT(o.id) as order_count"}) + .innerJoin("profiles p", "p.user_id = u.id") + .leftJoin("orders o", "o.user_id = u.id") + .where("u.active = true") + .where("u.created_at > '2023-01-01'") + .groupBy("u.id") + .having("COUNT(o.id) > 0") + .orderBy("order_count", "DESC") + .limit(10) + .build(); + + complexQuery->explain(); + std::cout << std::endl; + complexQuery->execute(); + + std::cout << "\n" << std::string(60, '=') << "\n" << std::endl; + + // Example 2: INSERT query + std::cout << "2. INSERT Query:" << std::endl; + auto insertQuery = SqlQueryBuilder("INSERT", "products") + .value("name", "Wireless Headphones") + .value("price", "99.99") + .value("category", "Electronics") + .value("stock_quantity", "150") + .build(); + + insertQuery->explain(); + std::cout << std::endl; + insertQuery->execute(); + + std::cout << "\n" << std::string(60, '=') << "\n" << std::endl; + + // Example 3: UPDATE query + std::cout << "3. UPDATE Query:" << std::endl; + auto updateQuery = SqlQueryBuilder("UPDATE", "products") + .set("price", "89.99") + .set("updated_at", "NOW()") + .whereEquals("category", "Electronics") + .where("stock_quantity < 10") + .build(); + + updateQuery->explain(); + std::cout << std::endl; + updateQuery->execute(); + + std::cout << "\n" << std::string(60, '=') << "\n" << std::endl; + + // Example 4: Using Director for common patterns + std::cout << "4. Using Director for Common Patterns:" << std::endl; + + std::cout << "\nSelect all users:" << std::endl; + auto allUsers = SqlQueryDirector::selectAllFromTable("users"); + allUsers->execute(); + + std::cout << "\nSelect specific user:" << std::endl; + auto specificUser = SqlQueryDirector::selectUserById(123); + specificUser->execute(); + + std::cout << "\nPaginated results:" << std::endl; + auto paginatedResults = SqlQueryDirector::paginatedSelect("products", 25, 2); + paginatedResults->execute(); + + std::cout << "\nCreate new user:" << std::endl; + auto newUser = SqlQueryDirector::createUser("john_doe", "john@example.com"); + newUser->execute(); + + // Example 5: Validation examples + std::cout << "\n" << std::string(60, '=') << "\n" << std::endl; + std::cout << "5. Validation Examples:" << std::endl; + + try { + auto invalidInsert = SqlQueryBuilder("INSERT", "users").build(); + } catch (const std::exception& e) { + std::cout << "❌ Build failed: " << e.what() << std::endl; + } + + try { + auto invalidLimit = SqlQueryBuilder("SELECT", "users").limit(-5).build(); + } catch (const std::exception& e) { + std::cout << "❌ Build failed: " << e.what() << std::endl; + } + + // This will show a warning but not fail + std::cout << "\nDangerous DELETE (shows warning):" << std::endl; + auto dangerousDelete = SqlQueryBuilder("DELETE", "temp_data").build(); + + std::cout << "\n✅ SQL Builder pattern successfully demonstrated!" << std::endl; + std::cout << "Benefits: Type safety, fluent interface, validation, SQL injection prevention" << std::endl; + + } catch (const std::exception& e) { + std::cerr << "❌ Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/content/design-pattern/code/builder/solution.java b/src/content/design-pattern/code/builder/solution.java new file mode 100644 index 0000000..6c3c787 --- /dev/null +++ b/src/content/design-pattern/code/builder/solution.java @@ -0,0 +1,236 @@ +/** + * Builder Pattern - Computer Configuration Example + * Constructs complex objects step by step with fluent interface and optional parameters + */ + +// Product class - Complex object being built +class Computer { + // Required parameters + private final String processor; + private final int ramGB; + + // Optional parameters - initialized with defaults + private final String motherboard; + private final String graphicsCard; + private final int storageGB; + private final String storageType; + private final boolean hasWiFi; + private final boolean hasBluetooth; + private final String powerSupply; + private final String caseType; + + private Computer(ComputerBuilder builder) { + this.processor = builder.processor; + this.ramGB = builder.ramGB; + this.motherboard = builder.motherboard; + this.graphicsCard = builder.graphicsCard; + this.storageGB = builder.storageGB; + this.storageType = builder.storageType; + this.hasWiFi = builder.hasWiFi; + this.hasBluetooth = builder.hasBluetooth; + this.powerSupply = builder.powerSupply; + this.caseType = builder.caseType; + } + + @Override + public String toString() { + return String.format(""" + Computer Configuration: + ├─ Processor: %s + ├─ RAM: %d GB + ├─ Motherboard: %s + ├─ Graphics: %s + ├─ Storage: %d GB %s + ├─ WiFi: %s + ├─ Bluetooth: %s + ├─ Power Supply: %s + └─ Case: %s + """, + processor, ramGB, motherboard, graphicsCard, + storageGB, storageType, hasWiFi ? "Yes" : "No", + hasBluetooth ? "Yes" : "No", powerSupply, caseType + ); + } + + public void startUp() { + System.out.println("🔧 Starting computer with " + processor + " and " + ramGB + "GB RAM"); + System.out.println("💾 Loading from " + storageGB + "GB " + storageType); + if (hasWiFi) System.out.println("📶 WiFi adapter detected"); + if (hasBluetooth) System.out.println("🔵 Bluetooth adapter detected"); + System.out.println("✅ Computer started successfully!"); + } + + // Builder class - Static nested class + public static class ComputerBuilder { + // Required parameters + private final String processor; + private final int ramGB; + + // Optional parameters with default values + private String motherboard = "Standard ATX"; + private String graphicsCard = "Integrated Graphics"; + private int storageGB = 256; + private String storageType = "SSD"; + private boolean hasWiFi = true; + private boolean hasBluetooth = false; + private String powerSupply = "500W"; + private String caseType = "Mid Tower"; + + // Constructor with required parameters + public ComputerBuilder(String processor, int ramGB) { + this.processor = processor; + this.ramGB = ramGB; + } + + // Fluent interface methods for optional parameters + public ComputerBuilder motherboard(String motherboard) { + this.motherboard = motherboard; + return this; + } + + public ComputerBuilder graphicsCard(String graphicsCard) { + this.graphicsCard = graphicsCard; + return this; + } + + public ComputerBuilder storage(int storageGB, String storageType) { + this.storageGB = storageGB; + this.storageType = storageType; + return this; + } + + public ComputerBuilder withWiFi(boolean hasWiFi) { + this.hasWiFi = hasWiFi; + return this; + } + + public ComputerBuilder withBluetooth(boolean hasBluetooth) { + this.hasBluetooth = hasBluetooth; + return this; + } + + public ComputerBuilder powerSupply(String powerSupply) { + this.powerSupply = powerSupply; + return this; + } + + public ComputerBuilder caseType(String caseType) { + this.caseType = caseType; + return this; + } + + // Build method to create the Computer instance + public Computer build() { + // Validation logic can be added here + if (ramGB < 4) { + throw new IllegalArgumentException("RAM must be at least 4GB"); + } + if (storageGB < 128) { + throw new IllegalArgumentException("Storage must be at least 128GB"); + } + + return new Computer(this); + } + } +} + +// Director class - Optional, provides specific configurations +class ComputerDirector { + public static Computer buildGamingPC() { + return new Computer.ComputerBuilder("Intel i9-12900K", 32) + .motherboard("ASUS ROG STRIX Z690-E") + .graphicsCard("NVIDIA RTX 4080") + .storage(1000, "NVMe SSD") + .withWiFi(true) + .withBluetooth(true) + .powerSupply("850W 80+ Gold") + .caseType("Full Tower RGB") + .build(); + } + + public static Computer buildOfficePC() { + return new Computer.ComputerBuilder("Intel i5-12400", 16) + .motherboard("MSI B660M Pro") + .storage(512, "SSD") + .withWiFi(true) + .withBluetooth(false) + .powerSupply("450W") + .caseType("Mini ITX") + .build(); + } + + public static Computer buildBudgetPC() { + return new Computer.ComputerBuilder("AMD Ryzen 5 5600G", 8) + .storage(256, "SSD") + .withWiFi(false) + .build(); // Uses default values for other components + } +} + +public class BuilderPatternDemo { + public static void main(String[] args) { + System.out.println("=== Builder Pattern Demo - Computer Configuration ===\n"); + + // Example 1: Step-by-step building with method chaining + System.out.println("1. Building Custom Gaming Computer:"); + Computer gamingPC = new Computer.ComputerBuilder("AMD Ryzen 9 7900X", 64) + .motherboard("ASUS ROG Crosshair X670E Hero") + .graphicsCard("NVIDIA RTX 4090") + .storage(2000, "NVMe SSD") + .withWiFi(true) + .withBluetooth(true) + .powerSupply("1000W 80+ Platinum") + .caseType("Full Tower Tempered Glass") + .build(); + + System.out.println(gamingPC); + gamingPC.startUp(); + + System.out.println("\n" + "=".repeat(60) + "\n"); + + // Example 2: Minimal configuration (using defaults) + System.out.println("2. Building Basic Computer (with defaults):"); + Computer basicPC = new Computer.ComputerBuilder("Intel i3-12100", 8) + .build(); + + System.out.println(basicPC); + basicPC.startUp(); + + System.out.println("\n" + "=".repeat(60) + "\n"); + + // Example 3: Using Director for pre-configured builds + System.out.println("3. Using Director for Pre-configured Builds:"); + + System.out.println("Gaming PC Configuration:"); + Computer directorGamingPC = ComputerDirector.buildGamingPC(); + System.out.println(directorGamingPC); + + System.out.println("Office PC Configuration:"); + Computer officePC = ComputerDirector.buildOfficePC(); + System.out.println(officePC); + + System.out.println("Budget PC Configuration:"); + Computer budgetPC = ComputerDirector.buildBudgetPC(); + System.out.println(budgetPC); + + // Example 4: Validation in action + System.out.println("\n4. Validation Example:"); + try { + Computer invalidPC = new Computer.ComputerBuilder("Intel i5", 2) // Too little RAM + .build(); + } catch (IllegalArgumentException e) { + System.out.println("❌ Build failed: " + e.getMessage()); + } + + try { + Computer invalidStorage = new Computer.ComputerBuilder("Intel i5", 8) + .storage(64, "HDD") // Too little storage + .build(); + } catch (IllegalArgumentException e) { + System.out.println("❌ Build failed: " + e.getMessage()); + } + + System.out.println("\n✅ Builder pattern successfully demonstrated!"); + System.out.println("Benefits: Fluent interface, optional parameters, validation, readability"); + } +} diff --git a/src/content/design-pattern/code/builder/solution.py b/src/content/design-pattern/code/builder/solution.py new file mode 100644 index 0000000..9f1285c --- /dev/null +++ b/src/content/design-pattern/code/builder/solution.py @@ -0,0 +1,325 @@ +""" +Builder Pattern - HTTP Request Builder Example +Constructs complex HTTP requests with optional parameters and validation +""" + +from typing import Dict, List, Optional, Any +from enum import Enum + +class HttpMethod(Enum): + GET = "GET" + POST = "POST" + PUT = "PUT" + DELETE = "DELETE" + PATCH = "PATCH" + +class HttpRequest: + """Complex HTTP Request object - the product being built""" + + def __init__(self, builder): + # Required parameters + self.url = builder.url + self.method = builder.method + + # Optional parameters + self.headers = builder.headers.copy() + self.query_params = builder.query_params.copy() + self.body = builder.body + self.timeout = builder.timeout + self.retries = builder.retries + self.auth = builder.auth + self.cookies = builder.cookies.copy() + self.follow_redirects = builder.follow_redirects + self.verify_ssl = builder.verify_ssl + + def __str__(self): + lines = [ + "HTTP Request Configuration:", + f"├─ Method: {self.method.value}", + f"├─ URL: {self.url}", + f"├─ Headers: {len(self.headers)} headers", + ] + + for key, value in self.headers.items(): + lines.append(f"│ ├─ {key}: {value}") + + lines.extend([ + f"├─ Query Params: {len(self.query_params)} parameters", + ]) + + for key, value in self.query_params.items(): + lines.append(f"│ ├─ {key}: {value}") + + lines.extend([ + f"├─ Body: {'Present' if self.body else 'None'}", + f"├─ Timeout: {self.timeout}s", + f"├─ Retries: {self.retries}", + f"├─ Auth: {'Configured' if self.auth else 'None'}", + f"├─ Cookies: {len(self.cookies)} cookies", + f"├─ Follow Redirects: {self.follow_redirects}", + f"└─ Verify SSL: {self.verify_ssl}" + ]) + + return "\n".join(lines) + + def execute(self): + """Simulate executing the HTTP request""" + print(f"🌐 Executing {self.method.value} request to {self.url}") + + if self.headers: + print(f"📋 Headers: {', '.join(self.headers.keys())}") + + if self.query_params: + query_string = '&'.join([f"{k}={v}" for k, v in self.query_params.items()]) + print(f"🔍 Query: ?{query_string}") + + if self.body: + print(f"📦 Request body: {len(str(self.body))} characters") + + if self.auth: + print(f"🔐 Authentication: {self.auth['type']}") + + print(f"⏱️ Timeout: {self.timeout}s, Retries: {self.retries}") + print("✅ Request executed successfully!") + +class HttpRequestBuilder: + """Builder for creating HTTP requests with fluent interface""" + + def __init__(self, url: str, method: HttpMethod = HttpMethod.GET): + # Required parameters + self.url = url + self.method = method + + # Optional parameters with defaults + self.headers: Dict[str, str] = {} + self.query_params: Dict[str, Any] = {} + self.body: Optional[Any] = None + self.timeout: int = 30 + self.retries: int = 3 + self.auth: Optional[Dict[str, Any]] = None + self.cookies: Dict[str, str] = {} + self.follow_redirects: bool = True + self.verify_ssl: bool = True + + def header(self, name: str, value: str): + """Add a single header""" + self.headers[name] = value + return self + + def headers(self, headers: Dict[str, str]): + """Add multiple headers""" + self.headers.update(headers) + return self + + def content_type(self, content_type: str): + """Convenience method for Content-Type header""" + self.headers["Content-Type"] = content_type + return self + + def json_content(self): + """Set Content-Type to application/json""" + return self.content_type("application/json") + + def form_content(self): + """Set Content-Type to application/x-www-form-urlencoded""" + return self.content_type("application/x-www-form-urlencoded") + + def query_param(self, name: str, value: Any): + """Add a single query parameter""" + self.query_params[name] = value + return self + + def query_params(self, params: Dict[str, Any]): + """Add multiple query parameters""" + self.query_params.update(params) + return self + + def json_body(self, data: Any): + """Set JSON body and content type""" + import json + self.body = json.dumps(data) + return self.json_content() + + def form_body(self, data: Dict[str, Any]): + """Set form body and content type""" + from urllib.parse import urlencode + self.body = urlencode(data) + return self.form_content() + + def raw_body(self, body: Any): + """Set raw body content""" + self.body = body + return self + + def timeout(self, seconds: int): + """Set request timeout""" + if seconds <= 0: + raise ValueError("Timeout must be positive") + self.timeout = seconds + return self + + def retries(self, count: int): + """Set retry count""" + if count < 0: + raise ValueError("Retry count cannot be negative") + self.retries = count + return self + + def basic_auth(self, username: str, password: str): + """Set basic authentication""" + self.auth = {"type": "Basic", "username": username, "password": password} + return self + + def bearer_token(self, token: str): + """Set bearer token authentication""" + self.auth = {"type": "Bearer", "token": token} + self.headers["Authorization"] = f"Bearer {token}" + return self + + def cookie(self, name: str, value: str): + """Add a single cookie""" + self.cookies[name] = value + return self + + def cookies(self, cookies: Dict[str, str]): + """Add multiple cookies""" + self.cookies.update(cookies) + return self + + def follow_redirects(self, follow: bool = True): + """Set whether to follow redirects""" + self.follow_redirects = follow + return self + + def verify_ssl(self, verify: bool = True): + """Set SSL verification""" + self.verify_ssl = verify + return self + + def build(self) -> HttpRequest: + """Build and validate the HTTP request""" + # Validation logic + if not self.url: + raise ValueError("URL is required") + + if not self.url.startswith(('http://', 'https://')): + raise ValueError("URL must start with http:// or https://") + + if self.method in [HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH] and not self.body: + print("⚠️ Warning: No body provided for", self.method.value, "request") + + # Add cookie header if cookies are present + if self.cookies: + cookie_string = '; '.join([f"{k}={v}" for k, v in self.cookies.items()]) + self.headers["Cookie"] = cookie_string + + return HttpRequest(self) + +class HttpRequestDirector: + """Director class providing common request configurations""" + + @staticmethod + def json_api_request(url: str, method: HttpMethod = HttpMethod.GET): + """Create a JSON API request with common headers""" + return (HttpRequestBuilder(url, method) + .json_content() + .header("Accept", "application/json") + .header("User-Agent", "HttpRequestBuilder/1.0") + .timeout(15) + .retries(2)) + + @staticmethod + def authenticated_api_request(url: str, token: str, method: HttpMethod = HttpMethod.GET): + """Create an authenticated API request""" + return (HttpRequestDirector.json_api_request(url, method) + .bearer_token(token) + .header("X-API-Version", "v1")) + + @staticmethod + def form_submission(url: str, form_data: Dict[str, Any]): + """Create a form submission request""" + return (HttpRequestBuilder(url, HttpMethod.POST) + .form_body(form_data) + .header("User-Agent", "Mozilla/5.0 (compatible; FormBuilder)") + .follow_redirects(True) + .timeout(30)) + +def main(): + print("=== Builder Pattern Demo - HTTP Request Builder ===\n") + + # Example 1: Simple GET request + print("1. Simple GET Request:") + simple_request = (HttpRequestBuilder("https://api.github.com/user") + .header("User-Agent", "MyApp/1.0") + .timeout(10) + .build()) + + print(simple_request) + simple_request.execute() + + print("\n" + "="*60 + "\n") + + # Example 2: Complex POST request with JSON + print("2. Complex JSON POST Request:") + api_data = { + "name": "John Doe", + "email": "john@example.com", + "role": "developer" + } + + complex_request = (HttpRequestBuilder("https://api.example.com/users", HttpMethod.POST) + .json_body(api_data) + .bearer_token("abc123token") + .header("X-Client-Version", "2.1.0") + .query_param("format", "json") + .query_param("include", "profile") + .cookie("session_id", "xyz789") + .timeout(20) + .retries(5) + .build()) + + print(complex_request) + complex_request.execute() + + print("\n" + "="*60 + "\n") + + # Example 3: Using Director for common patterns + print("3. Using Director for Common Patterns:") + + print("\nJSON API Request:") + json_request = (HttpRequestDirector.json_api_request("https://api.example.com/data") + .query_params({"limit": 10, "offset": 0}) + .build()) + print(json_request) + + print("\nAuthenticated API Request:") + auth_request = (HttpRequestDirector.authenticated_api_request( + "https://api.example.com/protected", "my-secret-token") + .query_param("expand", "details") + .build()) + print(auth_request) + + print("\nForm Submission:") + form_data = {"username": "user123", "password": "secret", "remember": "true"} + form_request = HttpRequestDirector.form_submission("https://example.com/login", form_data).build() + print(form_request) + + # Example 4: Validation + print("\n" + "="*60 + "\n") + print("4. Validation Examples:") + + try: + invalid_request = HttpRequestBuilder("").build() + except ValueError as e: + print(f"❌ Build failed: {e}") + + try: + invalid_url = HttpRequestBuilder("not-a-url").build() + except ValueError as e: + print(f"❌ Build failed: {e}") + + print("\n✅ Builder pattern successfully demonstrated!") + print("Benefits: Fluent interface, parameter validation, method chaining, readability") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/chain-of-responsibility/solution.cpp b/src/content/design-pattern/code/chain-of-responsibility/solution.cpp new file mode 100644 index 0000000..fc45dd7 --- /dev/null +++ b/src/content/design-pattern/code/chain-of-responsibility/solution.cpp @@ -0,0 +1,215 @@ +// Chain of Responsibility Pattern - Support Ticket System +// Handles different types of support requests through a chain of handlers + +#include +#include +#include +#include + +// Enums for ticket properties +enum class Priority { + LOW, + MEDIUM, + HIGH, + CRITICAL +}; + +enum class TicketType { + TECHNICAL, + BILLING, + GENERAL +}; + +// Support ticket class +class SupportTicket { +private: + std::string ticketId; + Priority priority; + TicketType type; + std::string description; + +public: + SupportTicket(const std::string& id, Priority p, TicketType t, const std::string& desc) + : ticketId(id), priority(p), type(t), description(desc) {} + + // Getters + std::string getTicketId() const { return ticketId; } + Priority getPriority() const { return priority; } + TicketType getType() const { return type; } + std::string getDescription() const { return description; } + + std::string toString() const { + std::string priorityStr = (priority == Priority::LOW) ? "LOW" : + (priority == Priority::MEDIUM) ? "MEDIUM" : + (priority == Priority::HIGH) ? "HIGH" : "CRITICAL"; + + std::string typeStr = (type == TicketType::TECHNICAL) ? "TECHNICAL" : + (type == TicketType::BILLING) ? "BILLING" : "GENERAL"; + + return "Ticket[" + ticketId + "]: " + typeStr + " - " + priorityStr + " (" + description + ")"; + } +}; + +// Abstract handler class +class SupportHandler { +protected: + std::shared_ptr nextHandler; + +public: + virtual ~SupportHandler() = default; + + void setNextHandler(std::shared_ptr handler) { + nextHandler = handler; + } + + virtual void handleRequest(const SupportTicket& ticket) = 0; +}; + +// Level 1 Support Handler - handles basic issues +class Level1SupportHandler : public SupportHandler { +public: + void handleRequest(const SupportTicket& ticket) override { + if (ticket.getPriority() == Priority::LOW && + ticket.getType() == TicketType::GENERAL) { + std::cout << "Level 1 Support: Handling ticket " << ticket.getTicketId() << std::endl; + std::cout << "Resolution: Provided FAQ link and basic troubleshooting" << std::endl; + std::cout << "Ticket resolved by Level 1 Support\n" << std::endl; + } else { + std::cout << "Level 1 Support: Escalating ticket " << ticket.getTicketId() << std::endl; + if (nextHandler) { + nextHandler->handleRequest(ticket); + } + } + } +}; + +// Level 2 Support Handler - handles technical and medium priority issues +class Level2SupportHandler : public SupportHandler { +public: + void handleRequest(const SupportTicket& ticket) override { + if ((ticket.getPriority() == Priority::MEDIUM && + ticket.getType() == TicketType::TECHNICAL) || + (ticket.getPriority() == Priority::LOW && + ticket.getType() == TicketType::BILLING)) { + std::cout << "Level 2 Support: Handling ticket " << ticket.getTicketId() << std::endl; + std::cout << "Resolution: Technical analysis completed, solution provided" << std::endl; + std::cout << "Ticket resolved by Level 2 Support\n" << std::endl; + } else { + std::cout << "Level 2 Support: Escalating ticket " << ticket.getTicketId() << std::endl; + if (nextHandler) { + nextHandler->handleRequest(ticket); + } + } + } +}; + +// Level 3 Support Handler - handles high priority and critical issues +class Level3SupportHandler : public SupportHandler { +public: + void handleRequest(const SupportTicket& ticket) override { + if (ticket.getPriority() == Priority::HIGH || + ticket.getPriority() == Priority::CRITICAL) { + std::cout << "Level 3 Support: Handling ticket " << ticket.getTicketId() << std::endl; + std::cout << "Resolution: Senior engineer assigned, comprehensive solution provided" << std::endl; + std::cout << "Ticket resolved by Level 3 Support\n" << std::endl; + } else { + std::cout << "Level 3 Support: Escalating ticket " << ticket.getTicketId() << std::endl; + if (nextHandler) { + nextHandler->handleRequest(ticket); + } + } + } +}; + +// Manager Handler - handles billing issues and unresolved tickets +class ManagerHandler : public SupportHandler { +public: + void handleRequest(const SupportTicket& ticket) override { + std::cout << "Manager: Handling ticket " << ticket.getTicketId() << std::endl; + if (ticket.getType() == TicketType::BILLING) { + std::cout << "Resolution: Billing dispute resolved, account adjusted" << std::endl; + } else { + std::cout << "Resolution: Escalated to specialized team, priority handling assigned" << std::endl; + } + std::cout << "Ticket resolved by Manager\n" << std::endl; + } +}; + +// Support ticket system class +class SupportTicketSystem { +private: + std::shared_ptr level1; + std::shared_ptr level2; + std::shared_ptr level3; + std::shared_ptr manager; + +public: + SupportTicketSystem() { + // Create the chain of handlers + level1 = std::make_shared(); + level2 = std::make_shared(); + level3 = std::make_shared(); + manager = std::make_shared(); + + // Set up the chain + level1->setNextHandler(level2); + level2->setNextHandler(level3); + level3->setNextHandler(manager); + } + + void processTicket(const SupportTicket& ticket) { + std::cout << "Processing: " << ticket.toString() << std::endl; + level1->handleRequest(ticket); + } +}; + +// Main demonstration +int main() { + std::cout << "=== Support Ticket System - Chain of Responsibility Pattern ===\n" << std::endl; + + // Create support ticket system + SupportTicketSystem supportSystem; + + // Create different types of support tickets + std::vector tickets = { + SupportTicket("T001", Priority::LOW, TicketType::GENERAL, + "How to reset password?"), + SupportTicket("T002", Priority::MEDIUM, TicketType::TECHNICAL, + "Application crashes on startup"), + SupportTicket("T003", Priority::HIGH, TicketType::TECHNICAL, + "Database connection issues"), + SupportTicket("T004", Priority::CRITICAL, TicketType::TECHNICAL, + "System down - production outage"), + SupportTicket("T005", Priority::MEDIUM, TicketType::BILLING, + "Incorrect charges on account"), + SupportTicket("T006", Priority::HIGH, TicketType::BILLING, + "Unauthorized transaction dispute") + }; + + // Process each ticket through the chain + for (const auto& ticket : tickets) { + supportSystem.processTicket(ticket); + } + + std::cout << "=== Chain of Responsibility Benefits ===" << std::endl; + std::cout << "1. Decoupling: Senders don't know which handler will process the request" << std::endl; + std::cout << "2. Flexibility: Easy to add/remove handlers without changing client code" << std::endl; + std::cout << "3. Responsibility: Each handler has a single responsibility" << std::endl; + std::cout << "4. Dynamic: Chain can be configured at runtime" << std::endl; + + // Demonstrate dynamic chain modification + std::cout << "\n=== Dynamic Chain Example ===" << std::endl; + + // Create a separate chain for VIP customers + auto vipHandler = std::make_shared(); + auto managerHandler = std::make_shared(); + vipHandler->setNextHandler(managerHandler); + + SupportTicket vipTicket("T007", Priority::MEDIUM, TicketType::TECHNICAL, + "VIP customer needs immediate assistance"); + + std::cout << "VIP Chain Processing: " << vipTicket.toString() << std::endl; + vipHandler->handleRequest(vipTicket); + + return 0; +} diff --git a/src/content/design-pattern/code/chain-of-responsibility/solution.java b/src/content/design-pattern/code/chain-of-responsibility/solution.java new file mode 100644 index 0000000..dd63bb5 --- /dev/null +++ b/src/content/design-pattern/code/chain-of-responsibility/solution.java @@ -0,0 +1,164 @@ +// Chain of Responsibility Pattern - Support Ticket System +// Handles different types of support requests through a chain of handlers + +import java.util.*; + +// Abstract handler class +abstract class SupportHandler { + protected SupportHandler nextHandler; + + public void setNextHandler(SupportHandler nextHandler) { + this.nextHandler = nextHandler; + } + + public abstract void handleRequest(SupportTicket ticket); +} + +// Support ticket class +class SupportTicket { + public enum Priority { + LOW, MEDIUM, HIGH, CRITICAL + } + + public enum Type { + TECHNICAL, BILLING, GENERAL + } + + private String ticketId; + private Priority priority; + private Type type; + private String description; + + public SupportTicket(String ticketId, Priority priority, Type type, String description) { + this.ticketId = ticketId; + this.priority = priority; + this.type = type; + this.description = description; + } + + // Getters + public String getTicketId() { return ticketId; } + public Priority getPriority() { return priority; } + public Type getType() { return type; } + public String getDescription() { return description; } + + @Override + public String toString() { + return String.format("Ticket[%s]: %s - %s (%s)", + ticketId, type, priority, description); + } +} + +// Level 1 Support Handler - handles basic issues +class Level1SupportHandler extends SupportHandler { + @Override + public void handleRequest(SupportTicket ticket) { + if (ticket.getPriority() == SupportTicket.Priority.LOW && + ticket.getType() == SupportTicket.Type.GENERAL) { + System.out.println("Level 1 Support: Handling ticket " + ticket.getTicketId()); + System.out.println("Resolution: Provided FAQ link and basic troubleshooting"); + System.out.println("Ticket resolved by Level 1 Support\n"); + } else { + System.out.println("Level 1 Support: Escalating ticket " + ticket.getTicketId()); + if (nextHandler != null) { + nextHandler.handleRequest(ticket); + } + } + } +} + +// Level 2 Support Handler - handles technical and medium priority issues +class Level2SupportHandler extends SupportHandler { + @Override + public void handleRequest(SupportTicket ticket) { + if ((ticket.getPriority() == SupportTicket.Priority.MEDIUM && + ticket.getType() == SupportTicket.Type.TECHNICAL) || + (ticket.getPriority() == SupportTicket.Priority.LOW && + ticket.getType() == SupportTicket.Type.BILLING)) { + System.out.println("Level 2 Support: Handling ticket " + ticket.getTicketId()); + System.out.println("Resolution: Technical analysis completed, solution provided"); + System.out.println("Ticket resolved by Level 2 Support\n"); + } else { + System.out.println("Level 2 Support: Escalating ticket " + ticket.getTicketId()); + if (nextHandler != null) { + nextHandler.handleRequest(ticket); + } + } + } +} + +// Level 3 Support Handler - handles high priority and critical issues +class Level3SupportHandler extends SupportHandler { + @Override + public void handleRequest(SupportTicket ticket) { + if (ticket.getPriority() == SupportTicket.Priority.HIGH || + ticket.getPriority() == SupportTicket.Priority.CRITICAL) { + System.out.println("Level 3 Support: Handling ticket " + ticket.getTicketId()); + System.out.println("Resolution: Senior engineer assigned, comprehensive solution provided"); + System.out.println("Ticket resolved by Level 3 Support\n"); + } else { + System.out.println("Level 3 Support: Escalating ticket " + ticket.getTicketId()); + if (nextHandler != null) { + nextHandler.handleRequest(ticket); + } + } + } +} + +// Manager Handler - handles billing issues and unresolved tickets +class ManagerHandler extends SupportHandler { + @Override + public void handleRequest(SupportTicket ticket) { + System.out.println("Manager: Handling ticket " + ticket.getTicketId()); + if (ticket.getType() == SupportTicket.Type.BILLING) { + System.out.println("Resolution: Billing dispute resolved, account adjusted"); + } else { + System.out.println("Resolution: Escalated to specialized team, priority handling assigned"); + } + System.out.println("Ticket resolved by Manager\n"); + } +} + +// Main demonstration class +public class ChainOfResponsibilityDemo { + public static void main(String[] args) { + // Create the chain of handlers + SupportHandler level1 = new Level1SupportHandler(); + SupportHandler level2 = new Level2SupportHandler(); + SupportHandler level3 = new Level3SupportHandler(); + SupportHandler manager = new ManagerHandler(); + + // Set up the chain + level1.setNextHandler(level2); + level2.setNextHandler(level3); + level3.setNextHandler(manager); + + System.out.println("=== Support Ticket System - Chain of Responsibility Pattern ===\n"); + + // Create different types of support tickets + List tickets = Arrays.asList( + new SupportTicket("T001", SupportTicket.Priority.LOW, + SupportTicket.Type.GENERAL, "How to reset password?"), + new SupportTicket("T002", SupportTicket.Priority.MEDIUM, + SupportTicket.Type.TECHNICAL, "Application crashes on startup"), + new SupportTicket("T003", SupportTicket.Priority.HIGH, + SupportTicket.Type.TECHNICAL, "Database connection issues"), + new SupportTicket("T004", SupportTicket.Priority.CRITICAL, + SupportTicket.Type.TECHNICAL, "System down - production outage"), + new SupportTicket("T005", SupportTicket.Priority.MEDIUM, + SupportTicket.Type.BILLING, "Incorrect charges on account") + ); + + // Process each ticket through the chain + for (SupportTicket ticket : tickets) { + System.out.println("Processing: " + ticket); + level1.handleRequest(ticket); + } + + System.out.println("=== Chain of Responsibility Benefits ==="); + System.out.println("1. Decoupling: Senders don't know which handler will process the request"); + System.out.println("2. Flexibility: Easy to add/remove handlers without changing client code"); + System.out.println("3. Responsibility: Each handler has a single responsibility"); + System.out.println("4. Dynamic: Chain can be configured at runtime"); + } +} diff --git a/src/content/design-pattern/code/chain-of-responsibility/solution.py b/src/content/design-pattern/code/chain-of-responsibility/solution.py new file mode 100644 index 0000000..def4259 --- /dev/null +++ b/src/content/design-pattern/code/chain-of-responsibility/solution.py @@ -0,0 +1,184 @@ +""" +Chain of Responsibility Pattern - Support Ticket System +Handles different types of support requests through a chain of handlers +""" + +from abc import ABC, abstractmethod +from enum import Enum +from typing import Optional, List + +class Priority(Enum): + LOW = "LOW" + MEDIUM = "MEDIUM" + HIGH = "HIGH" + CRITICAL = "CRITICAL" + +class TicketType(Enum): + TECHNICAL = "TECHNICAL" + BILLING = "BILLING" + GENERAL = "GENERAL" + +class SupportTicket: + """Represents a support ticket with priority and type""" + + def __init__(self, ticket_id: str, priority: Priority, ticket_type: TicketType, description: str): + self.ticket_id = ticket_id + self.priority = priority + self.ticket_type = ticket_type + self.description = description + + def __str__(self): + return f"Ticket[{self.ticket_id}]: {self.ticket_type.value} - {self.priority.value} ({self.description})" + +class SupportHandler(ABC): + """Abstract base class for support handlers""" + + def __init__(self): + self._next_handler: Optional[SupportHandler] = None + + def set_next_handler(self, handler: 'SupportHandler') -> None: + """Set the next handler in the chain""" + self._next_handler = handler + + @abstractmethod + def handle_request(self, ticket: SupportTicket) -> None: + """Handle the support ticket request""" + pass + +class Level1SupportHandler(SupportHandler): + """Level 1 Support - handles basic general inquiries""" + + def handle_request(self, ticket: SupportTicket) -> None: + if (ticket.priority == Priority.LOW and + ticket.ticket_type == TicketType.GENERAL): + print(f"Level 1 Support: Handling ticket {ticket.ticket_id}") + print("Resolution: Provided FAQ link and basic troubleshooting") + print("Ticket resolved by Level 1 Support\n") + else: + print(f"Level 1 Support: Escalating ticket {ticket.ticket_id}") + if self._next_handler: + self._next_handler.handle_request(ticket) + +class Level2SupportHandler(SupportHandler): + """Level 2 Support - handles technical and billing issues""" + + def handle_request(self, ticket: SupportTicket) -> None: + if ((ticket.priority == Priority.MEDIUM and + ticket.ticket_type == TicketType.TECHNICAL) or + (ticket.priority == Priority.LOW and + ticket.ticket_type == TicketType.BILLING)): + print(f"Level 2 Support: Handling ticket {ticket.ticket_id}") + print("Resolution: Technical analysis completed, solution provided") + print("Ticket resolved by Level 2 Support\n") + else: + print(f"Level 2 Support: Escalating ticket {ticket.ticket_id}") + if self._next_handler: + self._next_handler.handle_request(ticket) + +class Level3SupportHandler(SupportHandler): + """Level 3 Support - handles high priority and critical issues""" + + def handle_request(self, ticket: SupportTicket) -> None: + if (ticket.priority == Priority.HIGH or + ticket.priority == Priority.CRITICAL): + print(f"Level 3 Support: Handling ticket {ticket.ticket_id}") + print("Resolution: Senior engineer assigned, comprehensive solution provided") + print("Ticket resolved by Level 3 Support\n") + else: + print(f"Level 3 Support: Escalating ticket {ticket.ticket_id}") + if self._next_handler: + self._next_handler.handle_request(ticket) + +class ManagerHandler(SupportHandler): + """Manager - handles billing disputes and unresolved tickets""" + + def handle_request(self, ticket: SupportTicket) -> None: + print(f"Manager: Handling ticket {ticket.ticket_id}") + if ticket.ticket_type == TicketType.BILLING: + print("Resolution: Billing dispute resolved, account adjusted") + else: + print("Resolution: Escalated to specialized team, priority handling assigned") + print("Ticket resolved by Manager\n") + +class SupportTicketSystem: + """Support ticket system using chain of responsibility""" + + def __init__(self): + # Create the chain of handlers + self.level1 = Level1SupportHandler() + self.level2 = Level2SupportHandler() + self.level3 = Level3SupportHandler() + self.manager = ManagerHandler() + + # Set up the chain + self.level1.set_next_handler(self.level2) + self.level2.set_next_handler(self.level3) + self.level3.set_next_handler(self.manager) + + def process_ticket(self, ticket: SupportTicket) -> None: + """Process a support ticket through the chain""" + print(f"Processing: {ticket}") + self.level1.handle_request(ticket) + +def main(): + """Demonstrate the Chain of Responsibility pattern""" + print("=== Support Ticket System - Chain of Responsibility Pattern ===\n") + + # Create support ticket system + support_system = SupportTicketSystem() + + # Create different types of support tickets + tickets = [ + SupportTicket("T001", Priority.LOW, TicketType.GENERAL, + "How to reset password?"), + SupportTicket("T002", Priority.MEDIUM, TicketType.TECHNICAL, + "Application crashes on startup"), + SupportTicket("T003", Priority.HIGH, TicketType.TECHNICAL, + "Database connection issues"), + SupportTicket("T004", Priority.CRITICAL, TicketType.TECHNICAL, + "System down - production outage"), + SupportTicket("T005", Priority.MEDIUM, TicketType.BILLING, + "Incorrect charges on account"), + SupportTicket("T006", Priority.HIGH, TicketType.BILLING, + "Unauthorized transaction dispute") + ] + + # Process each ticket through the chain + for ticket in tickets: + support_system.process_ticket(ticket) + + print("=== Chain of Responsibility Benefits ===") + print("1. Decoupling: Senders don't know which handler will process the request") + print("2. Flexibility: Easy to add/remove handlers without changing client code") + print("3. Responsibility: Each handler has a single responsibility") + print("4. Dynamic: Chain can be configured at runtime") + + # Demonstrate dynamic chain modification + print("\n=== Dynamic Chain Modification ===") + print("Adding VIP Support Handler for high-priority billing issues...") + + class VIPSupportHandler(SupportHandler): + """VIP Support - handles high-priority billing issues""" + + def handle_request(self, ticket: SupportTicket) -> None: + if (ticket.priority == Priority.HIGH and + ticket.ticket_type == TicketType.BILLING): + print(f"VIP Support: Handling ticket {ticket.ticket_id}") + print("Resolution: VIP customer service, immediate account review") + print("Ticket resolved by VIP Support\n") + else: + if self._next_handler: + self._next_handler.handle_request(ticket) + + # Insert VIP handler into existing chain + vip_handler = VIPSupportHandler() + support_system.level2.set_next_handler(vip_handler) + vip_handler.set_next_handler(support_system.level3) + + # Test with VIP billing issue + vip_ticket = SupportTicket("T007", Priority.HIGH, TicketType.BILLING, + "VIP customer billing dispute") + support_system.process_ticket(vip_ticket) + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/command/solution.cpp b/src/content/design-pattern/code/command/solution.cpp new file mode 100644 index 0000000..3d90960 --- /dev/null +++ b/src/content/design-pattern/code/command/solution.cpp @@ -0,0 +1,310 @@ +// Command Pattern - Text Editor with Undo/Redo Functionality +// Encapsulates requests as objects, allowing for undo/redo operations and command queuing + +#include +#include +#include +#include +#include +#include + +// Abstract Command interface +class Command { +public: + virtual ~Command() = default; + virtual void execute() = 0; + virtual void undo() = 0; + virtual std::string getDescription() const = 0; +}; + +// Receiver class - Text Editor +class TextEditor { +private: + std::string content; + size_t cursorPosition; + +public: + TextEditor() : cursorPosition(0) {} + + void insertText(const std::string& text) { + content.insert(cursorPosition, text); + std::cout << "Inserted: '" << text << "' at position " << cursorPosition << std::endl; + cursorPosition += text.length(); + } + + void deleteText(size_t length) { + if (cursorPosition >= length) { + content.erase(cursorPosition - length, length); + cursorPosition -= length; + std::cout << "Deleted " << length << " characters" << std::endl; + } + } + + void moveCursor(size_t newPosition) { + if (newPosition <= content.length()) { + cursorPosition = newPosition; + std::cout << "Cursor moved to position " << cursorPosition << std::endl; + } + } + + const std::string& getContent() const { + return content; + } + + size_t getCursorPosition() const { + return cursorPosition; + } + + void displayContent() const { + std::cout << "Content: \"" << content << "\" (cursor at " << cursorPosition << ")" << std::endl; + } +}; + +// Concrete Commands +class InsertTextCommand : public Command { +private: + TextEditor* editor; + std::string text; + size_t previousPosition; + +public: + InsertTextCommand(TextEditor* ed, const std::string& txt) + : editor(ed), text(txt), previousPosition(0) {} + + void execute() override { + previousPosition = editor->getCursorPosition(); + editor->insertText(text); + } + + void undo() override { + editor->moveCursor(previousPosition + text.length()); + editor->deleteText(text.length()); + editor->moveCursor(previousPosition); + } + + std::string getDescription() const override { + return "Insert '" + text + "'"; + } +}; + +class DeleteTextCommand : public Command { +private: + TextEditor* editor; + size_t length; + std::string deletedText; + size_t previousPosition; + +public: + DeleteTextCommand(TextEditor* ed, size_t len) + : editor(ed), length(len), previousPosition(0) {} + + void execute() override { + previousPosition = editor->getCursorPosition(); + const std::string& content = editor->getContent(); + if (previousPosition >= length) { + deletedText = content.substr(previousPosition - length, length); + editor->deleteText(length); + } + } + + void undo() override { + if (!deletedText.empty()) { + editor->moveCursor(previousPosition - length); + editor->insertText(deletedText); + editor->moveCursor(previousPosition); + } + } + + std::string getDescription() const override { + return "Delete " + std::to_string(length) + " characters"; + } +}; + +class MoveCursorCommand : public Command { +private: + TextEditor* editor; + size_t newPosition; + size_t previousPosition; + +public: + MoveCursorCommand(TextEditor* ed, size_t pos) + : editor(ed), newPosition(pos), previousPosition(0) {} + + void execute() override { + previousPosition = editor->getCursorPosition(); + editor->moveCursor(newPosition); + } + + void undo() override { + editor->moveCursor(previousPosition); + } + + std::string getDescription() const override { + return "Move cursor to " + std::to_string(newPosition); + } +}; + +// Macro Command - combines multiple commands +class MacroCommand : public Command { +private: + std::vector> commands; + std::string name; + +public: + MacroCommand(const std::string& macroName) : name(macroName) {} + + void addCommand(std::unique_ptr command) { + commands.push_back(std::move(command)); + } + + void execute() override { + std::cout << "Executing macro: " << name << std::endl; + for (auto& command : commands) { + command->execute(); + } + } + + void undo() override { + std::cout << "Undoing macro: " << name << std::endl; + // Undo commands in reverse order + for (auto it = commands.rbegin(); it != commands.rend(); ++it) { + (*it)->undo(); + } + } + + std::string getDescription() const override { + return "Macro: " + name + " (" + std::to_string(commands.size()) + " commands)"; + } +}; + +// Invoker - Command Manager +class CommandManager { +private: + std::stack> undoStack; + std::stack> redoStack; + std::queue> commandQueue; + +public: + void executeCommand(std::unique_ptr command) { + command->execute(); + std::cout << "Command executed: " << command->getDescription() << std::endl; + undoStack.push(std::move(command)); + + // Clear redo stack when new command is executed + while (!redoStack.empty()) { + redoStack.pop(); + } + } + + void undo() { + if (!undoStack.empty()) { + auto command = std::move(undoStack.top()); + undoStack.pop(); + std::cout << "Undid: " << command->getDescription() << std::endl; + command->undo(); + redoStack.push(std::move(command)); + } else { + std::cout << "Nothing to undo" << std::endl; + } + } + + void redo() { + if (!redoStack.empty()) { + auto command = std::move(redoStack.top()); + redoStack.pop(); + std::cout << "Redid: " << command->getDescription() << std::endl; + command->execute(); + undoStack.push(std::move(command)); + } else { + std::cout << "Nothing to redo" << std::endl; + } + } + + void queueCommand(std::unique_ptr command) { + std::cout << "Queued command: " << command->getDescription() << std::endl; + commandQueue.push(std::move(command)); + } + + void executeQueuedCommands() { + std::cout << "Executing queued commands..." << std::endl; + while (!commandQueue.empty()) { + auto command = std::move(commandQueue.front()); + commandQueue.pop(); + executeCommand(std::move(command)); + } + } +}; + +// Demonstration +int main() { + std::cout << "=== Command Pattern - Text Editor Demo ===\n" << std::endl; + + // Create receiver and invoker + TextEditor editor; + CommandManager commandManager; + + std::cout << "1. Basic Command Operations:" << std::endl; + editor.displayContent(); + + // Create and execute commands + commandManager.executeCommand(std::make_unique(&editor, "Hello")); + editor.displayContent(); + + commandManager.executeCommand(std::make_unique(&editor, " ")); + editor.displayContent(); + + commandManager.executeCommand(std::make_unique(&editor, "World")); + editor.displayContent(); + + commandManager.executeCommand(std::make_unique(&editor, "!")); + editor.displayContent(); + + std::cout << "\n2. Undo Operations:" << std::endl; + commandManager.undo(); + editor.displayContent(); + + commandManager.undo(); + editor.displayContent(); + + std::cout << "\n3. Redo Operations:" << std::endl; + commandManager.redo(); + editor.displayContent(); + + commandManager.redo(); + editor.displayContent(); + + std::cout << "\n4. Cursor Movement and Deletion:" << std::endl; + commandManager.executeCommand(std::make_unique(&editor, 5)); + editor.displayContent(); + + commandManager.executeCommand(std::make_unique(&editor, 3)); + editor.displayContent(); + + std::cout << "\n5. Macro Command (Insert signature):" << std::endl; + auto insertSignature = std::make_unique("Insert Signature"); + insertSignature->addCommand(std::make_unique(&editor, editor.getContent().length())); + insertSignature->addCommand(std::make_unique(&editor, "\\n\\nBest regards,\\nJohn Doe")); + + commandManager.executeCommand(std::move(insertSignature)); + editor.displayContent(); + + std::cout << "\n6. Undo Macro:" << std::endl; + commandManager.undo(); + editor.displayContent(); + + std::cout << "\n7. Command Queuing:" << std::endl; + commandManager.queueCommand(std::make_unique(&editor, "\\nPS: ")); + commandManager.queueCommand(std::make_unique(&editor, "This is a postscript.")); + commandManager.executeQueuedCommands(); + editor.displayContent(); + + std::cout << "\n=== Command Pattern Benefits ===" << std::endl; + std::cout << "1. Decoupling: Invoker doesn't need to know about receiver implementation" << std::endl; + std::cout << "2. Undo/Redo: Commands can be reversed, enabling undo functionality" << std::endl; + std::cout << "3. Logging: Commands can be logged for auditing or replay" << std::endl; + std::cout << "4. Queuing: Commands can be queued and executed later" << std::endl; + std::cout << "5. Macro Commands: Multiple commands can be combined into composite commands" << std::endl; + std::cout << "6. Remote Execution: Commands can be serialized and sent over network" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/command/solution.java b/src/content/design-pattern/code/command/solution.java new file mode 100644 index 0000000..25f421c --- /dev/null +++ b/src/content/design-pattern/code/command/solution.java @@ -0,0 +1,335 @@ +// Command Pattern - Text Editor with Undo/Redo Functionality +// Encapsulates requests as objects, allowing for undo/redo operations and command queuing + +import java.util.*; + +// Command interface +interface Command { + void execute(); + void undo(); + String getDescription(); +} + +// Receiver class - Text Editor +class TextEditor { + private StringBuilder content; + private int cursorPosition; + + public TextEditor() { + this.content = new StringBuilder(); + this.cursorPosition = 0; + } + + public void insertText(String text) { + content.insert(cursorPosition, text); + cursorPosition += text.length(); + System.out.println("Inserted: '" + text + "' at position " + (cursorPosition - text.length())); + } + + public void deleteText(int length) { + if (cursorPosition >= length) { + content.delete(cursorPosition - length, cursorPosition); + cursorPosition -= length; + System.out.println("Deleted " + length + " characters"); + } + } + + public void moveCursor(int newPosition) { + if (newPosition >= 0 && newPosition <= content.length()) { + cursorPosition = newPosition; + System.out.println("Cursor moved to position " + cursorPosition); + } + } + + public String getContent() { + return content.toString(); + } + + public int getCursorPosition() { + return cursorPosition; + } + + public void displayContent() { + System.out.println("Content: \"" + content.toString() + "\" (cursor at " + cursorPosition + ")"); + } +} + +// Concrete Commands +class InsertTextCommand implements Command { + private TextEditor editor; + private String text; + private int previousPosition; + + public InsertTextCommand(TextEditor editor, String text) { + this.editor = editor; + this.text = text; + } + + @Override + public void execute() { + previousPosition = editor.getCursorPosition(); + editor.insertText(text); + } + + @Override + public void undo() { + editor.moveCursor(previousPosition + text.length()); + editor.deleteText(text.length()); + editor.moveCursor(previousPosition); + } + + @Override + public String getDescription() { + return "Insert '" + text + "'"; + } +} + +class DeleteTextCommand implements Command { + private TextEditor editor; + private int length; + private String deletedText; + private int previousPosition; + + public DeleteTextCommand(TextEditor editor, int length) { + this.editor = editor; + this.length = length; + } + + @Override + public void execute() { + previousPosition = editor.getCursorPosition(); + String content = editor.getContent(); + if (previousPosition >= length) { + deletedText = content.substring(previousPosition - length, previousPosition); + editor.deleteText(length); + } + } + + @Override + public void undo() { + if (deletedText != null) { + editor.moveCursor(previousPosition - length); + editor.insertText(deletedText); + editor.moveCursor(previousPosition); + } + } + + @Override + public String getDescription() { + return "Delete " + length + " characters"; + } +} + +class MoveCursorCommand implements Command { + private TextEditor editor; + private int newPosition; + private int previousPosition; + + public MoveCursorCommand(TextEditor editor, int newPosition) { + this.editor = editor; + this.newPosition = newPosition; + } + + @Override + public void execute() { + previousPosition = editor.getCursorPosition(); + editor.moveCursor(newPosition); + } + + @Override + public void undo() { + editor.moveCursor(previousPosition); + } + + @Override + public String getDescription() { + return "Move cursor to " + newPosition; + } +} + +// Macro Command - combines multiple commands +class MacroCommand implements Command { + private List commands; + private String name; + + public MacroCommand(String name) { + this.name = name; + this.commands = new ArrayList<>(); + } + + public void addCommand(Command command) { + commands.add(command); + } + + @Override + public void execute() { + System.out.println("Executing macro: " + name); + for (Command command : commands) { + command.execute(); + } + } + + @Override + public void undo() { + System.out.println("Undoing macro: " + name); + for (int i = commands.size() - 1; i >= 0; i--) { + commands.get(i).undo(); + } + } + + @Override + public String getDescription() { + return "Macro: " + name + " (" + commands.size() + " commands)"; + } +} + +// Invoker - Command Manager +class CommandManager { + private Stack undoStack; + private Stack redoStack; + private Queue commandQueue; + + public CommandManager() { + this.undoStack = new Stack<>(); + this.redoStack = new Stack<>(); + this.commandQueue = new LinkedList<>(); + } + + public void executeCommand(Command command) { + command.execute(); + undoStack.push(command); + redoStack.clear(); // Clear redo stack when new command is executed + System.out.println("Command executed: " + command.getDescription()); + } + + public void undo() { + if (!undoStack.isEmpty()) { + Command command = undoStack.pop(); + command.undo(); + redoStack.push(command); + System.out.println("Undid: " + command.getDescription()); + } else { + System.out.println("Nothing to undo"); + } + } + + public void redo() { + if (!redoStack.isEmpty()) { + Command command = redoStack.pop(); + command.execute(); + undoStack.push(command); + System.out.println("Redid: " + command.getDescription()); + } else { + System.out.println("Nothing to redo"); + } + } + + public void queueCommand(Command command) { + commandQueue.offer(command); + System.out.println("Queued command: " + command.getDescription()); + } + + public void executeQueuedCommands() { + System.out.println("Executing queued commands..."); + while (!commandQueue.isEmpty()) { + Command command = commandQueue.poll(); + executeCommand(command); + } + } + + public void showHistory() { + System.out.println("Command History:"); + if (undoStack.isEmpty()) { + System.out.println(" No commands in history"); + } else { + for (int i = undoStack.size() - 1; i >= 0; i--) { + System.out.println(" " + (undoStack.size() - i) + ". " + + undoStack.get(i).getDescription()); + } + } + } +} + +// Main demonstration class +public class CommandPatternDemo { + public static void main(String[] args) { + System.out.println("=== Command Pattern - Text Editor Demo ===\n"); + + // Create receiver and invoker + TextEditor editor = new TextEditor(); + CommandManager commandManager = new CommandManager(); + + System.out.println("1. Basic Command Operations:"); + editor.displayContent(); + + // Create and execute commands + Command insertHello = new InsertTextCommand(editor, "Hello"); + Command insertSpace = new InsertTextCommand(editor, " "); + Command insertWorld = new InsertTextCommand(editor, "World"); + Command insertExclamation = new InsertTextCommand(editor, "!"); + + commandManager.executeCommand(insertHello); + editor.displayContent(); + + commandManager.executeCommand(insertSpace); + editor.displayContent(); + + commandManager.executeCommand(insertWorld); + editor.displayContent(); + + commandManager.executeCommand(insertExclamation); + editor.displayContent(); + + System.out.println("\n2. Undo Operations:"); + commandManager.undo(); + editor.displayContent(); + + commandManager.undo(); + editor.displayContent(); + + System.out.println("\n3. Redo Operations:"); + commandManager.redo(); + editor.displayContent(); + + commandManager.redo(); + editor.displayContent(); + + System.out.println("\n4. Cursor Movement and Deletion:"); + Command moveCursor = new MoveCursorCommand(editor, 5); + commandManager.executeCommand(moveCursor); + editor.displayContent(); + + Command deleteCommand = new DeleteTextCommand(editor, 3); + commandManager.executeCommand(deleteCommand); + editor.displayContent(); + + System.out.println("\n5. Command History:"); + commandManager.showHistory(); + + System.out.println("\n6. Macro Command (Insert signature):"); + MacroCommand insertSignature = new MacroCommand("Insert Signature"); + insertSignature.addCommand(new MoveCursorCommand(editor, editor.getContent().length())); + insertSignature.addCommand(new InsertTextCommand(editor, "\n\nBest regards,\nJohn Doe")); + + commandManager.executeCommand(insertSignature); + editor.displayContent(); + + System.out.println("\n7. Undo Macro:"); + commandManager.undo(); + editor.displayContent(); + + System.out.println("\n8. Command Queuing:"); + commandManager.queueCommand(new InsertTextCommand(editor, "\nPS: ")); + commandManager.queueCommand(new InsertTextCommand(editor, "This is a postscript.")); + commandManager.executeQueuedCommands(); + editor.displayContent(); + + System.out.println("\n=== Command Pattern Benefits ==="); + System.out.println("1. Decoupling: Invoker doesn't need to know about receiver implementation"); + System.out.println("2. Undo/Redo: Commands can be reversed, enabling undo functionality"); + System.out.println("3. Logging: Commands can be logged for auditing or replay"); + System.out.println("4. Queuing: Commands can be queued and executed later"); + System.out.println("5. Macro Commands: Multiple commands can be combined into composite commands"); + System.out.println("6. Remote Execution: Commands can be serialized and sent over network"); + } +} diff --git a/src/content/design-pattern/code/command/solution.py b/src/content/design-pattern/code/command/solution.py new file mode 100644 index 0000000..af728fc --- /dev/null +++ b/src/content/design-pattern/code/command/solution.py @@ -0,0 +1,311 @@ +""" +Command Pattern - Text Editor with Undo/Redo Functionality +Encapsulates requests as objects, allowing for undo/redo operations and command queuing +""" + +from abc import ABC, abstractmethod +from typing import List, Deque +from collections import deque + +class Command(ABC): + """Abstract command interface""" + + @abstractmethod + def execute(self) -> None: + """Execute the command""" + pass + + @abstractmethod + def undo(self) -> None: + """Undo the command""" + pass + + @abstractmethod + def get_description(self) -> str: + """Get command description""" + pass + +class TextEditor: + """Receiver class - Text Editor that performs the actual operations""" + + def __init__(self): + self._content = "" + self._cursor_position = 0 + + def insert_text(self, text: str) -> None: + """Insert text at cursor position""" + self._content = (self._content[:self._cursor_position] + + text + + self._content[self._cursor_position:]) + print(f"Inserted: '{text}' at position {self._cursor_position}") + self._cursor_position += len(text) + + def delete_text(self, length: int) -> None: + """Delete text before cursor position""" + if self._cursor_position >= length: + self._content = (self._content[:self._cursor_position - length] + + self._content[self._cursor_position:]) + self._cursor_position -= length + print(f"Deleted {length} characters") + + def move_cursor(self, new_position: int) -> None: + """Move cursor to new position""" + if 0 <= new_position <= len(self._content): + self._cursor_position = new_position + print(f"Cursor moved to position {self._cursor_position}") + + @property + def content(self) -> str: + """Get current content""" + return self._content + + @property + def cursor_position(self) -> int: + """Get current cursor position""" + return self._cursor_position + + def display_content(self) -> None: + """Display current content with cursor position""" + print(f'Content: "{self._content}" (cursor at {self._cursor_position})') + +# Concrete Command Classes +class InsertTextCommand(Command): + """Command to insert text""" + + def __init__(self, editor: TextEditor, text: str): + self._editor = editor + self._text = text + self._previous_position = 0 + + def execute(self) -> None: + self._previous_position = self._editor.cursor_position + self._editor.insert_text(self._text) + + def undo(self) -> None: + self._editor.move_cursor(self._previous_position + len(self._text)) + self._editor.delete_text(len(self._text)) + self._editor.move_cursor(self._previous_position) + + def get_description(self) -> str: + return f"Insert '{self._text}'" + +class DeleteTextCommand(Command): + """Command to delete text""" + + def __init__(self, editor: TextEditor, length: int): + self._editor = editor + self._length = length + self._deleted_text = "" + self._previous_position = 0 + + def execute(self) -> None: + self._previous_position = self._editor.cursor_position + if self._previous_position >= self._length: + start = self._previous_position - self._length + self._deleted_text = self._editor.content[start:self._previous_position] + self._editor.delete_text(self._length) + + def undo(self) -> None: + if self._deleted_text: + self._editor.move_cursor(self._previous_position - self._length) + self._editor.insert_text(self._deleted_text) + self._editor.move_cursor(self._previous_position) + + def get_description(self) -> str: + return f"Delete {self._length} characters" + +class MoveCursorCommand(Command): + """Command to move cursor""" + + def __init__(self, editor: TextEditor, new_position: int): + self._editor = editor + self._new_position = new_position + self._previous_position = 0 + + def execute(self) -> None: + self._previous_position = self._editor.cursor_position + self._editor.move_cursor(self._new_position) + + def undo(self) -> None: + self._editor.move_cursor(self._previous_position) + + def get_description(self) -> str: + return f"Move cursor to {self._new_position}" + +class MacroCommand(Command): + """Composite command that combines multiple commands""" + + def __init__(self, name: str): + self._name = name + self._commands: List[Command] = [] + + def add_command(self, command: Command) -> None: + """Add a command to the macro""" + self._commands.append(command) + + def execute(self) -> None: + print(f"Executing macro: {self._name}") + for command in self._commands: + command.execute() + + def undo(self) -> None: + print(f"Undoing macro: {self._name}") + # Undo commands in reverse order + for command in reversed(self._commands): + command.undo() + + def get_description(self) -> str: + return f"Macro: {self._name} ({len(self._commands)} commands)" + +class CommandManager: + """Invoker class that manages command execution, undo, and redo""" + + def __init__(self): + self._undo_stack: List[Command] = [] + self._redo_stack: List[Command] = [] + self._command_queue: Deque[Command] = deque() + + def execute_command(self, command: Command) -> None: + """Execute a command and add to undo stack""" + command.execute() + self._undo_stack.append(command) + self._redo_stack.clear() # Clear redo stack when new command is executed + print(f"Command executed: {command.get_description()}") + + def undo(self) -> None: + """Undo the last command""" + if self._undo_stack: + command = self._undo_stack.pop() + command.undo() + self._redo_stack.append(command) + print(f"Undid: {command.get_description()}") + else: + print("Nothing to undo") + + def redo(self) -> None: + """Redo the last undone command""" + if self._redo_stack: + command = self._redo_stack.pop() + command.execute() + self._undo_stack.append(command) + print(f"Redid: {command.get_description()}") + else: + print("Nothing to redo") + + def queue_command(self, command: Command) -> None: + """Add command to queue for batch execution""" + self._command_queue.append(command) + print(f"Queued command: {command.get_description()}") + + def execute_queued_commands(self) -> None: + """Execute all queued commands""" + print("Executing queued commands...") + while self._command_queue: + command = self._command_queue.popleft() + self.execute_command(command) + + def show_history(self) -> None: + """Show command execution history""" + print("Command History:") + if not self._undo_stack: + print(" No commands in history") + else: + for i, command in enumerate(reversed(self._undo_stack), 1): + print(f" {i}. {command.get_description()}") + +def main(): + """Demonstrate the Command pattern""" + print("=== Command Pattern - Text Editor Demo ===\n") + + # Create receiver and invoker + editor = TextEditor() + command_manager = CommandManager() + + print("1. Basic Command Operations:") + editor.display_content() + + # Create and execute commands + insert_hello = InsertTextCommand(editor, "Hello") + insert_space = InsertTextCommand(editor, " ") + insert_world = InsertTextCommand(editor, "World") + insert_exclamation = InsertTextCommand(editor, "!") + + command_manager.execute_command(insert_hello) + editor.display_content() + + command_manager.execute_command(insert_space) + editor.display_content() + + command_manager.execute_command(insert_world) + editor.display_content() + + command_manager.execute_command(insert_exclamation) + editor.display_content() + + print("\n2. Undo Operations:") + command_manager.undo() + editor.display_content() + + command_manager.undo() + editor.display_content() + + print("\n3. Redo Operations:") + command_manager.redo() + editor.display_content() + + command_manager.redo() + editor.display_content() + + print("\n4. Cursor Movement and Deletion:") + move_cursor = MoveCursorCommand(editor, 5) + command_manager.execute_command(move_cursor) + editor.display_content() + + delete_command = DeleteTextCommand(editor, 3) + command_manager.execute_command(delete_command) + editor.display_content() + + print("\n5. Command History:") + command_manager.show_history() + + print("\n6. Macro Command (Insert signature):") + insert_signature = MacroCommand("Insert Signature") + insert_signature.add_command(MoveCursorCommand(editor, len(editor.content))) + insert_signature.add_command(InsertTextCommand(editor, "\n\nBest regards,\nJohn Doe")) + + command_manager.execute_command(insert_signature) + editor.display_content() + + print("\n7. Undo Macro:") + command_manager.undo() + editor.display_content() + + print("\n8. Command Queuing:") + command_manager.queue_command(InsertTextCommand(editor, "\nPS: ")) + command_manager.queue_command(InsertTextCommand(editor, "This is a postscript.")) + command_manager.execute_queued_commands() + editor.display_content() + + print("\n9. Advanced Macro with Mixed Commands:") + format_text = MacroCommand("Format Text") + format_text.add_command(MoveCursorCommand(editor, 0)) + format_text.add_command(InsertTextCommand(editor, "*** ")) + format_text.add_command(MoveCursorCommand(editor, len(editor.content))) + format_text.add_command(InsertTextCommand(editor, " ***")) + + command_manager.execute_command(format_text) + editor.display_content() + + print("\n10. Final Command History:") + command_manager.show_history() + + print("\n=== Command Pattern Benefits ===") + print("1. Decoupling: Invoker doesn't need to know about receiver implementation") + print("2. Undo/Redo: Commands can be reversed, enabling undo functionality") + print("3. Logging: Commands can be logged for auditing or replay") + print("4. Queuing: Commands can be queued and executed later") + print("5. Macro Commands: Multiple commands can be combined into composite commands") + print("6. Remote Execution: Commands can be serialized and sent over network") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/composite/solution.cpp b/src/content/design-pattern/code/composite/solution.cpp new file mode 100644 index 0000000..24dc1dc --- /dev/null +++ b/src/content/design-pattern/code/composite/solution.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +// Component abstract class +class FileSystemComponent { +public: + virtual ~FileSystemComponent() = default; + virtual void showDetails() const = 0; + virtual int getSize() const = 0; +}; + +// Leaf class - File +class File : public FileSystemComponent { +private: + std::string name; + int size; + +public: + File(const std::string& name, int size) : name(name), size(size) {} + + void showDetails() const override { + std::cout << "File: " << name << " (Size: " << size << " KB)" << std::endl; + } + + int getSize() const override { + return size; + } +}; + +// Composite class - Directory +class Directory : public FileSystemComponent { +private: + std::string name; + std::vector> components; + +public: + Directory(const std::string& name) : name(name) {} + + void addComponent(std::unique_ptr component) { + components.push_back(std::move(component)); + } + + void showDetails() const override { + std::cout << "Directory: " << name << " (Total Size: " << getSize() << " KB)" << std::endl; + for (const auto& component : components) { + std::cout << " "; + component->showDetails(); + } + } + + int getSize() const override { + int totalSize = 0; + for (const auto& component : components) { + totalSize += component->getSize(); + } + return totalSize; + } +}; + +int main() { + // Create files + auto file1 = std::make_unique("document.txt", 10); + auto file2 = std::make_unique("image.png", 25); + auto file3 = std::make_unique("video.mp4", 100); + auto file4 = std::make_unique("presentation.ppt", 50); + + // Create directories + auto documents = std::make_unique("Documents"); + auto media = std::make_unique("Media"); + auto root = std::make_unique("Root"); + + // Build the tree structure + documents->addComponent(std::make_unique("document.txt", 10)); + documents->addComponent(std::make_unique("presentation.ppt", 50)); + + media->addComponent(std::make_unique("image.png", 25)); + media->addComponent(std::make_unique("video.mp4", 100)); + + root->addComponent(std::move(documents)); + root->addComponent(std::move(media)); + + // Display the entire structure + root->showDetails(); + + return 0; +} diff --git a/src/content/design-pattern/code/composite/solution.java b/src/content/design-pattern/code/composite/solution.java new file mode 100644 index 0000000..7cd6e67 --- /dev/null +++ b/src/content/design-pattern/code/composite/solution.java @@ -0,0 +1,93 @@ +import java.util.*; + +// Component interface +interface FileSystemComponent { + void showDetails(); + int getSize(); +} + +// Leaf class - File +class File implements FileSystemComponent { + private String name; + private int size; + + public File(String name, int size) { + this.name = name; + this.size = size; + } + + @Override + public void showDetails() { + System.out.println("File: " + name + " (Size: " + size + " KB)"); + } + + @Override + public int getSize() { + return size; + } +} + +// Composite class - Directory +class Directory implements FileSystemComponent { + private String name; + private List components; + + public Directory(String name) { + this.name = name; + this.components = new ArrayList<>(); + } + + public void addComponent(FileSystemComponent component) { + components.add(component); + } + + public void removeComponent(FileSystemComponent component) { + components.remove(component); + } + + @Override + public void showDetails() { + System.out.println("Directory: " + name + " (Total Size: " + getSize() + " KB)"); + for (FileSystemComponent component : components) { + System.out.print(" "); + component.showDetails(); + } + } + + @Override + public int getSize() { + int totalSize = 0; + for (FileSystemComponent component : components) { + totalSize += component.getSize(); + } + return totalSize; + } +} + +public class CompositePatternDemo { + public static void main(String[] args) { + // Create files + File file1 = new File("document.txt", 10); + File file2 = new File("image.png", 25); + File file3 = new File("video.mp4", 100); + File file4 = new File("presentation.ppt", 50); + + // Create directories + Directory documents = new Directory("Documents"); + Directory media = new Directory("Media"); + Directory root = new Directory("Root"); + + // Build the tree structure + documents.addComponent(file1); + documents.addComponent(file4); + + media.addComponent(file2); + media.addComponent(file3); + + root.addComponent(documents); + root.addComponent(media); + + // Display the entire structure + root.showDetails(); + } +} diff --git a/src/content/design-pattern/code/composite/solution.py b/src/content/design-pattern/code/composite/solution.py new file mode 100644 index 0000000..6e3396e --- /dev/null +++ b/src/content/design-pattern/code/composite/solution.py @@ -0,0 +1,76 @@ +from abc import ABC, abstractmethod +from typing import List + +# Component abstract class +class FileSystemComponent(ABC): + @abstractmethod + def show_details(self) -> None: + pass + + @abstractmethod + def get_size(self) -> int: + pass + +# Leaf class - File +class File(FileSystemComponent): + def __init__(self, name: str, size: int): + self.name = name + self.size = size + + def show_details(self) -> None: + print(f"File: {self.name} (Size: {self.size} KB)") + + def get_size(self) -> int: + return self.size + +# Composite class - Directory +class Directory(FileSystemComponent): + def __init__(self, name: str): + self.name = name + self.components: List[FileSystemComponent] = [] + + def add_component(self, component: FileSystemComponent) -> None: + self.components.append(component) + + def remove_component(self, component: FileSystemComponent) -> None: + self.components.remove(component) + + def show_details(self) -> None: + print(f"Directory: {self.name} (Total Size: {self.get_size()} KB)") + for component in self.components: + print(" ", end="") + component.show_details() + + def get_size(self) -> int: + total_size = 0 + for component in self.components: + total_size += component.get_size() + return total_size + +def main(): + # Create files + file1 = File("document.txt", 10) + file2 = File("image.png", 25) + file3 = File("video.mp4", 100) + file4 = File("presentation.ppt", 50) + + # Create directories + documents = Directory("Documents") + media = Directory("Media") + root = Directory("Root") + + # Build the tree structure + documents.add_component(file1) + documents.add_component(file4) + + media.add_component(file2) + media.add_component(file3) + + root.add_component(documents) + root.add_component(media) + + # Display the entire structure + root.show_details() + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/decorator/solution.cpp b/src/content/design-pattern/code/decorator/solution.cpp new file mode 100644 index 0000000..9305b14 --- /dev/null +++ b/src/content/design-pattern/code/decorator/solution.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + +// Component interface +class DataSource { +public: + virtual ~DataSource() = default; + virtual void writeData(const std::string& data) = 0; + virtual std::string readData() = 0; +}; + +// Concrete Component +class FileDataSource : public DataSource { +private: + std::string filename; + +public: + FileDataSource(const std::string& filename) : filename(filename) {} + + void writeData(const std::string& data) override { + std::cout << "Writing data to file: " << filename << " -> " << data << std::endl; + } + + std::string readData() override { + return "Data from file: " + filename; + } +}; + +// Base Decorator +class DataSourceDecorator : public DataSource { +protected: + std::unique_ptr wrappee; + +public: + DataSourceDecorator(std::unique_ptr source) : wrappee(std::move(source)) {} + + void writeData(const std::string& data) override { + wrappee->writeData(data); + } + + std::string readData() override { + return wrappee->readData(); + } +}; + +// Concrete Decorators +class EncryptionDecorator : public DataSourceDecorator { +private: + std::string encrypt(const std::string& data) { + return "encrypted(" + data + ")"; + } + + std::string decrypt(const std::string& data) { + std::string result = data; + size_t start = result.find("encrypted("); + if (start != std::string::npos) { + result.erase(start, 10); // Remove "encrypted(" + size_t end = result.find_last_of(')'); + if (end != std::string::npos) { + result.erase(end, 1); // Remove ")" + } + } + return result; + } + +public: + EncryptionDecorator(std::unique_ptr source) : DataSourceDecorator(std::move(source)) {} + + void writeData(const std::string& data) override { + std::string encryptedData = encrypt(data); + DataSourceDecorator::writeData(encryptedData); + } + + std::string readData() override { + std::string data = DataSourceDecorator::readData(); + return decrypt(data); + } +}; + +class CompressionDecorator : public DataSourceDecorator { +private: + std::string compress(const std::string& data) { + return "compressed(" + data + ")"; + } + + std::string decompress(const std::string& data) { + std::string result = data; + size_t start = result.find("compressed("); + if (start != std::string::npos) { + result.erase(start, 11); // Remove "compressed(" + size_t end = result.find_last_of(')'); + if (end != std::string::npos) { + result.erase(end, 1); // Remove ")" + } + } + return result; + } + +public: + CompressionDecorator(std::unique_ptr source) : DataSourceDecorator(std::move(source)) {} + + void writeData(const std::string& data) override { + std::string compressedData = compress(data); + DataSourceDecorator::writeData(compressedData); + } + + std::string readData() override { + std::string data = DataSourceDecorator::readData(); + return decompress(data); + } +}; + +int main() { + // Simple file data source + auto source = std::make_unique("data.txt"); + source->writeData("Hello World"); + std::cout << "Read: " << source->readData() << std::endl; + + std::cout << "\n--- With Encryption ---" << std::endl; + // Wrap with encryption + auto encryptedSource = std::make_unique( + std::make_unique("data.txt") + ); + encryptedSource->writeData("Sensitive Data"); + std::cout << "Read: " << encryptedSource->readData() << std::endl; + + std::cout << "\n--- With Compression and Encryption ---" << std::endl; + // Wrap with both compression and encryption + auto decoratedSource = std::make_unique( + std::make_unique( + std::make_unique("secure_data.txt") + ) + ); + decoratedSource->writeData("Large sensitive data"); + std::cout << "Read: " << decoratedSource->readData() << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/decorator/solution.java b/src/content/design-pattern/code/decorator/solution.java new file mode 100644 index 0000000..4c8b789 --- /dev/null +++ b/src/content/design-pattern/code/decorator/solution.java @@ -0,0 +1,122 @@ +// Component interface +interface DataSource { + void writeData(String data); + String readData(); +} + +// Concrete Component +class FileDataSource implements DataSource { + private String filename; + + public FileDataSource(String filename) { + this.filename = filename; + } + + @Override + public void writeData(String data) { + System.out.println("Writing data to file: " + filename + " -> " + data); + } + + @Override + public String readData() { + return "Data from file: " + filename; + } +} + +// Base Decorator +abstract class DataSourceDecorator implements DataSource { + protected DataSource wrappee; + + public DataSourceDecorator(DataSource source) { + this.wrappee = source; + } + + @Override + public void writeData(String data) { + wrappee.writeData(data); + } + + @Override + public String readData() { + return wrappee.readData(); + } +} + +// Concrete Decorators +class EncryptionDecorator extends DataSourceDecorator { + public EncryptionDecorator(DataSource source) { + super(source); + } + + @Override + public void writeData(String data) { + String encryptedData = encrypt(data); + super.writeData(encryptedData); + } + + @Override + public String readData() { + String data = super.readData(); + return decrypt(data); + } + + private String encrypt(String data) { + return "encrypted(" + data + ")"; + } + + private String decrypt(String data) { + return data.replace("encrypted(", "").replace(")", ""); + } +} + +class CompressionDecorator extends DataSourceDecorator { + public CompressionDecorator(DataSource source) { + super(source); + } + + @Override + public void writeData(String data) { + String compressedData = compress(data); + super.writeData(compressedData); + } + + @Override + public String readData() { + String data = super.readData(); + return decompress(data); + } + + private String compress(String data) { + return "compressed(" + data + ")"; + } + + private String decompress(String data) { + return data.replace("compressed(", "").replace(")", ""); + } +} + +// Client code +public class DecoratorPatternDemo { + public static void main(String[] args) { + // Simple file data source + DataSource source = new FileDataSource("data.txt"); + source.writeData("Hello World"); + System.out.println("Read: " + source.readData()); + + System.out.println("\n--- With Encryption ---"); + // Wrap with encryption + DataSource encryptedSource = new EncryptionDecorator(source); + encryptedSource.writeData("Sensitive Data"); + System.out.println("Read: " + encryptedSource.readData()); + + System.out.println("\n--- With Compression and Encryption ---"); + // Wrap with both compression and encryption + DataSource decoratedSource = new CompressionDecorator( + new EncryptionDecorator( + new FileDataSource("secure_data.txt") + ) + ); + decoratedSource.writeData("Large sensitive data"); + System.out.println("Read: " + decoratedSource.readData()); + } +} diff --git a/src/content/design-pattern/code/decorator/solution.py b/src/content/design-pattern/code/decorator/solution.py new file mode 100644 index 0000000..d7bd503 --- /dev/null +++ b/src/content/design-pattern/code/decorator/solution.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod + +# Component interface +class DataSource(ABC): + @abstractmethod + def write_data(self, data: str) -> None: + pass + + @abstractmethod + def read_data(self) -> str: + pass + +# Concrete Component +class FileDataSource(DataSource): + def __init__(self, filename: str): + self.filename = filename + + def write_data(self, data: str) -> None: + print(f"Writing data to file: {self.filename} -> {data}") + + def read_data(self) -> str: + return f"Data from file: {self.filename}" + +# Base Decorator +class DataSourceDecorator(DataSource): + def __init__(self, source: DataSource): + self._wrappee = source + + def write_data(self, data: str) -> None: + self._wrappee.write_data(data) + + def read_data(self) -> str: + return self._wrappee.read_data() + +# Concrete Decorators +class EncryptionDecorator(DataSourceDecorator): + def write_data(self, data: str) -> None: + encrypted_data = self._encrypt(data) + super().write_data(encrypted_data) + + def read_data(self) -> str: + data = super().read_data() + return self._decrypt(data) + + def _encrypt(self, data: str) -> str: + return f"encrypted({data})" + + def _decrypt(self, data: str) -> str: + return data.replace("encrypted(", "").replace(")", "") + +class CompressionDecorator(DataSourceDecorator): + def write_data(self, data: str) -> None: + compressed_data = self._compress(data) + super().write_data(compressed_data) + + def read_data(self) -> str: + data = super().read_data() + return self._decompress(data) + + def _compress(self, data: str) -> str: + return f"compressed({data})" + + def _decompress(self, data: str) -> str: + return data.replace("compressed(", "").replace(")", "") + +class LoggingDecorator(DataSourceDecorator): + def write_data(self, data: str) -> None: + print(f"[LOG] Writing data: {data}") + super().write_data(data) + + def read_data(self) -> str: + print("[LOG] Reading data") + data = super().read_data() + print(f"[LOG] Data read: {data}") + return data + +# Client code +def main(): + # Simple file data source + source = FileDataSource("data.txt") + source.write_data("Hello World") + print(f"Read: {source.read_data()}") + + print("\n--- With Encryption ---") + # Wrap with encryption + encrypted_source = EncryptionDecorator(source) + encrypted_source.write_data("Sensitive Data") + print(f"Read: {encrypted_source.read_data()}") + + print("\n--- With Multiple Decorators ---") + # Wrap with multiple decorators + decorated_source = LoggingDecorator( + CompressionDecorator( + EncryptionDecorator( + FileDataSource("secure_data.txt") + ) + ) + ) + decorated_source.write_data("Large sensitive data") + print(f"Read: {decorated_source.read_data()}") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/facade/solution.cpp b/src/content/design-pattern/code/facade/solution.cpp new file mode 100644 index 0000000..39b60e9 --- /dev/null +++ b/src/content/design-pattern/code/facade/solution.cpp @@ -0,0 +1,240 @@ +#include +#include + +// Complex subsystem classes +class AudioSystem { +public: + void turnOn() { + std::cout << "Audio System: Turning on..." << std::endl; + } + + void setVolume(int volume) { + std::cout << "Audio System: Setting volume to " << volume << std::endl; + } + + void setSource(const std::string& source) { + std::cout << "Audio System: Setting source to " << source << std::endl; + } + + void turnOff() { + std::cout << "Audio System: Turning off..." << std::endl; + } +}; + +class VideoProjector { +public: + void turnOn() { + std::cout << "Video Projector: Turning on..." << std::endl; + } + + void setInput(const std::string& input) { + std::cout << "Video Projector: Setting input to " << input << std::endl; + } + + void setResolution(const std::string& resolution) { + std::cout << "Video Projector: Setting resolution to " << resolution << std::endl; + } + + void turnOff() { + std::cout << "Video Projector: Turning off..." << std::endl; + } +}; + +class LightingSystem { +public: + void turnOn() { + std::cout << "Lighting System: Turning on..." << std::endl; + } + + void setBrightness(int brightness) { + std::cout << "Lighting System: Setting brightness to " << brightness << "%" << std::endl; + } + + void setColor(const std::string& color) { + std::cout << "Lighting System: Setting color to " << color << std::endl; + } + + void turnOff() { + std::cout << "Lighting System: Turning off..." << std::endl; + } +}; + +class ClimateControl { +public: + void turnOn() { + std::cout << "Climate Control: Turning on..." << std::endl; + } + + void setTemperature(int temperature) { + std::cout << "Climate Control: Setting temperature to " << temperature << "°F" << std::endl; + } + + void turnOff() { + std::cout << "Climate Control: Turning off..." << std::endl; + } +}; + +class SecuritySystem { +public: + void armSystem() { + std::cout << "Security System: Arming security system..." << std::endl; + } + + void disarmSystem() { + std::cout << "Security System: Disarming security system..." << std::endl; + } + + void setMode(const std::string& mode) { + std::cout << "Security System: Setting mode to " << mode << std::endl; + } +}; + +class StreamingDevice { +public: + void turnOn() { + std::cout << "Streaming Device: Powering on..." << std::endl; + } + + void connectToWiFi() { + std::cout << "Streaming Device: Connecting to WiFi..." << std::endl; + } + + void launchApp(const std::string& app) { + std::cout << "Streaming Device: Launching " << app << " app..." << std::endl; + } + + void turnOff() { + std::cout << "Streaming Device: Turning off..." << std::endl; + } +}; + +// Facade class that provides simple interface +class SmartHomeFacade { +private: + AudioSystem audioSystem; + VideoProjector projector; + LightingSystem lights; + ClimateControl climate; + SecuritySystem security; + StreamingDevice streaming; + +public: + void startMovieNight() { + std::cout << "\n=== STARTING MOVIE NIGHT MODE ===" << std::endl; + + // Set up lighting + lights.turnOn(); + lights.setBrightness(20); + lights.setColor("warm white"); + + // Set up audio/video + audioSystem.turnOn(); + audioSystem.setVolume(75); + audioSystem.setSource("HDMI"); + + projector.turnOn(); + projector.setInput("HDMI"); + projector.setResolution("4K"); + + // Start streaming + streaming.turnOn(); + streaming.connectToWiFi(); + streaming.launchApp("Netflix"); + + // Set comfortable temperature + climate.turnOn(); + climate.setTemperature(72); + + // Set security to home mode + security.setMode("Home"); + + std::cout << "Movie night mode activated! Enjoy your movie!\n" << std::endl; + } + + void startPartyMode() { + std::cout << "\n=== STARTING PARTY MODE ===" << std::endl; + + // Bright, colorful lighting + lights.turnOn(); + lights.setBrightness(100); + lights.setColor("party colors"); + + // Loud music setup + audioSystem.turnOn(); + audioSystem.setVolume(90); + audioSystem.setSource("Bluetooth"); + + // Turn off projector for party + projector.turnOff(); + + // Set party temperature + climate.turnOn(); + climate.setTemperature(68); + + // Disable security for guests + security.disarmSystem(); + + std::cout << "Party mode activated! Let's party!\n" << std::endl; + } + + void startSleepMode() { + std::cout << "\n=== STARTING SLEEP MODE ===" << std::endl; + + // Dim lighting gradually + lights.setBrightness(5); + lights.setColor("red"); + + // Turn off entertainment systems + audioSystem.turnOff(); + projector.turnOff(); + streaming.turnOff(); + + // Set sleep temperature + climate.setTemperature(65); + + // Arm security system + security.armSystem(); + + std::cout << "Sleep mode activated! Good night!\n" << std::endl; + } + + void leaveHome() { + std::cout << "\n=== LEAVING HOME ===" << std::endl; + + // Turn off all systems + lights.turnOff(); + audioSystem.turnOff(); + projector.turnOff(); + streaming.turnOff(); + climate.turnOff(); + + // Arm security system + security.armSystem(); + + std::cout << "Home secured! All systems turned off.\n" << std::endl; + } +}; + +int main() { + SmartHomeFacade smartHome; + + std::cout << "Welcome to your Smart Home!" << std::endl; + + // Demonstrate different scenarios + smartHome.startMovieNight(); + smartHome.startPartyMode(); + smartHome.startSleepMode(); + smartHome.leaveHome(); + + std::cout << "Without the facade, you would need to manually control:" << std::endl; + std::cout << "- Audio System (4 methods)" << std::endl; + std::cout << "- Video Projector (4 methods)" << std::endl; + std::cout << "- Lighting System (4 methods)" << std::endl; + std::cout << "- Climate Control (3 methods)" << std::endl; + std::cout << "- Security System (3 methods)" << std::endl; + std::cout << "- Streaming Device (4 methods)" << std::endl; + std::cout << "Total: 22 method calls for each scenario!" << std::endl; + std::cout << "The facade reduces this to just 1 method call per scenario." << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/facade/solution.java b/src/content/design-pattern/code/facade/solution.java new file mode 100644 index 0000000..99d9f84 --- /dev/null +++ b/src/content/design-pattern/code/facade/solution.java @@ -0,0 +1,293 @@ +// Complex subsystem classes +class AudioSystem { + public void turnOn() { + System.out.println("Audio System: Turning on..."); + } + + public void setVolume(int volume) { + System.out.println("Audio System: Setting volume to " + volume); + } + + public void setSource(String source) { + System.out.println("Audio System: Setting source to " + source); + } + + public void turnOff() { + System.out.println("Audio System: Turning off..."); + } +} + +class VideoProjector { + public void turnOn() { + System.out.println("Video Projector: Turning on..."); + } + + public void setInput(String input) { + System.out.println("Video Projector: Setting input to " + input); + } + + public void setResolution(String resolution) { + System.out.println("Video Projector: Setting resolution to " + resolution); + } + + public void turnOff() { + System.out.println("Video Projector: Turning off..."); + } +} + +class LightingSystem { + public void turnOn() { + System.out.println("Lighting System: Turning on..."); + } + + public void setBrightness(int brightness) { + System.out.println("Lighting System: Setting brightness to " + brightness + "%"); + } + + public void setColor(String color) { + System.out.println("Lighting System: Setting color to " + color); + } + + public void turnOff() { + System.out.println("Lighting System: Turning off..."); + } +} + +class ClimateControl { + public void turnOn() { + System.out.println("Climate Control: Turning on..."); + } + + public void setTemperature(int temperature) { + System.out.println("Climate Control: Setting temperature to " + temperature + "°F"); + } + + public void turnOff() { + System.out.println("Climate Control: Turning off..."); + } +} + +class SecuritySystem { + public void armSystem() { + System.out.println("Security System: Arming security system..."); + } + + public void disarmSystem() { + System.out.println("Security System: Disarming security system..."); + } + + public void setMode(String mode) { + System.out.println("Security System: Setting mode to " + mode); + } +} + +class StreamingDevice { + public void turnOn() { + System.out.println("Streaming Device: Powering on..."); + } + + public void connectToWiFi() { + System.out.println("Streaming Device: Connecting to WiFi..."); + } + + public void launchApp(String app) { + System.out.println("Streaming Device: Launching " + app + " app..."); + } + + public void turnOff() { + System.out.println("Streaming Device: Turning off..."); + } +} + +// Facade class that provides simple interface +class SmartHomeFacade { + private AudioSystem audioSystem; + private VideoProjector projector; + private LightingSystem lights; + private ClimateControl climate; + private SecuritySystem security; + private StreamingDevice streaming; + + public SmartHomeFacade() { + this.audioSystem = new AudioSystem(); + this.projector = new VideoProjector(); + this.lights = new LightingSystem(); + this.climate = new ClimateControl(); + this.security = new SecuritySystem(); + this.streaming = new StreamingDevice(); + } + + // Simple methods that hide complex subsystem interactions + public void startMovieNight() { + System.out.println("\n=== STARTING MOVIE NIGHT MODE ==="); + + // Set up lighting + lights.turnOn(); + lights.setBrightness(20); + lights.setColor("warm white"); + + // Set up audio/video + audioSystem.turnOn(); + audioSystem.setVolume(75); + audioSystem.setSource("HDMI"); + + projector.turnOn(); + projector.setInput("HDMI"); + projector.setResolution("4K"); + + // Start streaming + streaming.turnOn(); + streaming.connectToWiFi(); + streaming.launchApp("Netflix"); + + // Set comfortable temperature + climate.turnOn(); + climate.setTemperature(72); + + // Set security to home mode + security.setMode("Home"); + + System.out.println("Movie night mode activated! Enjoy your movie!\n"); + } + + public void startPartyMode() { + System.out.println("\n=== STARTING PARTY MODE ==="); + + // Bright, colorful lighting + lights.turnOn(); + lights.setBrightness(100); + lights.setColor("party colors"); + + // Loud music setup + audioSystem.turnOn(); + audioSystem.setVolume(90); + audioSystem.setSource("Bluetooth"); + + // Turn off projector for party + projector.turnOff(); + + // Set party temperature + climate.turnOn(); + climate.setTemperature(68); + + // Disable security for guests + security.disarmSystem(); + + System.out.println("Party mode activated! Let's party!\n"); + } + + public void startSleepMode() { + System.out.println("\n=== STARTING SLEEP MODE ==="); + + // Dim lighting gradually + lights.setBrightness(5); + lights.setColor("red"); + + // Turn off entertainment systems + audioSystem.turnOff(); + projector.turnOff(); + streaming.turnOff(); + + // Set sleep temperature + climate.setTemperature(65); + + // Arm security system + security.armSystem(); + + System.out.println("Sleep mode activated! Good night!\n"); + } + + public void startWorkMode() { + System.out.println("\n=== STARTING WORK MODE ==="); + + // Bright, productive lighting + lights.turnOn(); + lights.setBrightness(80); + lights.setColor("cool white"); + + // Quiet background audio + audioSystem.turnOn(); + audioSystem.setVolume(30); + audioSystem.setSource("Spotify"); + + // Set comfortable work temperature + climate.turnOn(); + climate.setTemperature(70); + + // Home security mode + security.setMode("Home"); + + System.out.println("Work mode activated! Time to be productive!\n"); + } + + public void leaveHome() { + System.out.println("\n=== LEAVING HOME ==="); + + // Turn off all systems + lights.turnOff(); + audioSystem.turnOff(); + projector.turnOff(); + streaming.turnOff(); + climate.turnOff(); + + // Arm security system + security.armSystem(); + + System.out.println("Home secured! All systems turned off.\n"); + } + + public void arriveHome() { + System.out.println("\n=== ARRIVING HOME ==="); + + // Disarm security + security.disarmSystem(); + + // Turn on basic lighting + lights.turnOn(); + lights.setBrightness(60); + lights.setColor("warm white"); + + // Set comfortable temperature + climate.turnOn(); + climate.setTemperature(72); + + System.out.println("Welcome home! Basic systems activated.\n"); + } +} + +public class FacadePatternDemo { + public static void main(String[] args) { + SmartHomeFacade smartHome = new SmartHomeFacade(); + + // Demonstrate different scenarios using simple facade methods + System.out.println("Welcome to your Smart Home!"); + + // Arrive home + smartHome.arriveHome(); + + // Start work session + smartHome.startWorkMode(); + + // Movie time + smartHome.startMovieNight(); + + // Party time + smartHome.startPartyMode(); + + // Time for bed + smartHome.startSleepMode(); + + // Leave home + smartHome.leaveHome(); + + System.out.println("\nWithout the facade, you would need to manually control:"); + System.out.println("- Audio System (4 methods)"); + System.out.println("- Video Projector (4 methods)"); + System.out.println("- Lighting System (4 methods)"); + System.out.println("- Climate Control (3 methods)"); + System.out.println("- Security System (3 methods)"); + System.out.println("- Streaming Device (4 methods)"); + System.out.println("Total: 22 method calls for each scenario!"); + System.out.println("The facade reduces this to just 1 method call per scenario."); + } +} diff --git a/src/content/design-pattern/code/facade/solution.py b/src/content/design-pattern/code/facade/solution.py new file mode 100644 index 0000000..3871048 --- /dev/null +++ b/src/content/design-pattern/code/facade/solution.py @@ -0,0 +1,197 @@ +# Complex subsystem classes +class AudioSystem: + def turn_on(self): + print("Audio System: Turning on...") + + def set_volume(self, volume): + print(f"Audio System: Setting volume to {volume}") + + def set_source(self, source): + print(f"Audio System: Setting source to {source}") + + def turn_off(self): + print("Audio System: Turning off...") + +class VideoProjector: + def turn_on(self): + print("Video Projector: Turning on...") + + def set_input(self, input_source): + print(f"Video Projector: Setting input to {input_source}") + + def set_resolution(self, resolution): + print(f"Video Projector: Setting resolution to {resolution}") + + def turn_off(self): + print("Video Projector: Turning off...") + +class LightingSystem: + def turn_on(self): + print("Lighting System: Turning on...") + + def set_brightness(self, brightness): + print(f"Lighting System: Setting brightness to {brightness}%") + + def set_color(self, color): + print(f"Lighting System: Setting color to {color}") + + def turn_off(self): + print("Lighting System: Turning off...") + +class ClimateControl: + def turn_on(self): + print("Climate Control: Turning on...") + + def set_temperature(self, temperature): + print(f"Climate Control: Setting temperature to {temperature}°F") + + def turn_off(self): + print("Climate Control: Turning off...") + +class SecuritySystem: + def arm_system(self): + print("Security System: Arming security system...") + + def disarm_system(self): + print("Security System: Disarming security system...") + + def set_mode(self, mode): + print(f"Security System: Setting mode to {mode}") + +class StreamingDevice: + def turn_on(self): + print("Streaming Device: Powering on...") + + def connect_to_wifi(self): + print("Streaming Device: Connecting to WiFi...") + + def launch_app(self, app): + print(f"Streaming Device: Launching {app} app...") + + def turn_off(self): + print("Streaming Device: Turning off...") + +# Facade class that provides simple interface +class SmartHomeFacade: + def __init__(self): + self.audio_system = AudioSystem() + self.projector = VideoProjector() + self.lights = LightingSystem() + self.climate = ClimateControl() + self.security = SecuritySystem() + self.streaming = StreamingDevice() + + def start_movie_night(self): + print("\n=== STARTING MOVIE NIGHT MODE ===") + + # Set up lighting + self.lights.turn_on() + self.lights.set_brightness(20) + self.lights.set_color("warm white") + + # Set up audio/video + self.audio_system.turn_on() + self.audio_system.set_volume(75) + self.audio_system.set_source("HDMI") + + self.projector.turn_on() + self.projector.set_input("HDMI") + self.projector.set_resolution("4K") + + # Start streaming + self.streaming.turn_on() + self.streaming.connect_to_wifi() + self.streaming.launch_app("Netflix") + + # Set comfortable temperature + self.climate.turn_on() + self.climate.set_temperature(72) + + # Set security to home mode + self.security.set_mode("Home") + + print("Movie night mode activated! Enjoy your movie!\n") + + def start_party_mode(self): + print("\n=== STARTING PARTY MODE ===") + + # Bright, colorful lighting + self.lights.turn_on() + self.lights.set_brightness(100) + self.lights.set_color("party colors") + + # Loud music setup + self.audio_system.turn_on() + self.audio_system.set_volume(90) + self.audio_system.set_source("Bluetooth") + + # Turn off projector for party + self.projector.turn_off() + + # Set party temperature + self.climate.turn_on() + self.climate.set_temperature(68) + + # Disable security for guests + self.security.disarm_system() + + print("Party mode activated! Let's party!\n") + + def start_sleep_mode(self): + print("\n=== STARTING SLEEP MODE ===") + + # Dim lighting gradually + self.lights.set_brightness(5) + self.lights.set_color("red") + + # Turn off entertainment systems + self.audio_system.turn_off() + self.projector.turn_off() + self.streaming.turn_off() + + # Set sleep temperature + self.climate.set_temperature(65) + + # Arm security system + self.security.arm_system() + + print("Sleep mode activated! Good night!\n") + + def leave_home(self): + print("\n=== LEAVING HOME ===") + + # Turn off all systems + self.lights.turn_off() + self.audio_system.turn_off() + self.projector.turn_off() + self.streaming.turn_off() + self.climate.turn_off() + + # Arm security system + self.security.arm_system() + + print("Home secured! All systems turned off.\n") + +def main(): + smart_home = SmartHomeFacade() + + print("Welcome to your Smart Home!") + + # Demonstrate different scenarios + smart_home.start_movie_night() + smart_home.start_party_mode() + smart_home.start_sleep_mode() + smart_home.leave_home() + + print("Without the facade, you would need to manually control:") + print("- Audio System (4 methods)") + print("- Video Projector (4 methods)") + print("- Lighting System (4 methods)") + print("- Climate Control (3 methods)") + print("- Security System (3 methods)") + print("- Streaming Device (4 methods)") + print("Total: 22 method calls for each scenario!") + print("The facade reduces this to just 1 method call per scenario.") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/factory-method/solution.cpp b/src/content/design-pattern/code/factory-method/solution.cpp new file mode 100644 index 0000000..c1edb48 --- /dev/null +++ b/src/content/design-pattern/code/factory-method/solution.cpp @@ -0,0 +1,181 @@ +#include +#include +#include +#include + +// Product interface +class Vehicle { +public: + virtual ~Vehicle() = default; + virtual void start() = 0; + virtual void stop() = 0; + virtual std::string getType() = 0; + virtual int getMaxSpeed() = 0; +}; + +// Concrete Products +class Car : public Vehicle { +public: + void start() override { + std::cout << "🚗 Car engine starting..." << std::endl; + } + + void stop() override { + std::cout << "🚗 Car engine stopping..." << std::endl; + } + + std::string getType() override { + return "Car"; + } + + int getMaxSpeed() override { + return 200; // km/h + } +}; + +class Motorcycle : public Vehicle { +public: + void start() override { + std::cout << "🏍️ Motorcycle engine roaring to life..." << std::endl; + } + + void stop() override { + std::cout << "🏍️ Motorcycle engine turning off..." << std::endl; + } + + std::string getType() override { + return "Motorcycle"; + } + + int getMaxSpeed() override { + return 300; // km/h + } +}; + +class Truck : public Vehicle { +public: + void start() override { + std::cout << "🚚 Truck diesel engine starting..." << std::endl; + } + + void stop() override { + std::cout << "🚚 Truck diesel engine stopping..." << std::endl; + } + + std::string getType() override { + return "Truck"; + } + + int getMaxSpeed() override { + return 120; // km/h + } +}; + +class Bicycle : public Vehicle { +public: + void start() override { + std::cout << "🚲 Ready to pedal!" << std::endl; + } + + void stop() override { + std::cout << "🚲 Stopped pedaling." << std::endl; + } + + std::string getType() override { + return "Bicycle"; + } + + int getMaxSpeed() override { + return 50; // km/h + } +}; + +// Creator abstract class +class VehicleFactory { +public: + virtual ~VehicleFactory() = default; + + // Factory method - to be implemented by subclasses + virtual std::unique_ptr createVehicle() = 0; + + // Template method that uses the factory method + void processVehicle() { + std::cout << "\n=== Processing Vehicle ===" << std::endl; + auto vehicle = createVehicle(); + + std::cout << "Vehicle Created: " << vehicle->getType() << std::endl; + std::cout << "Max Speed: " << vehicle->getMaxSpeed() << " km/h" << std::endl; + + vehicle->start(); + std::cout << "Vehicle is now operational!" << std::endl; + + // Simulate usage + std::cout << "Using vehicle for transportation..." << std::endl; + + vehicle->stop(); + std::cout << "Vehicle processing completed.\n" << std::endl; + } +}; + +// Concrete Creators +class CarFactory : public VehicleFactory { +public: + std::unique_ptr createVehicle() override { + std::cout << "🏭 Car Factory: Creating a new car..." << std::endl; + return std::make_unique(); + } +}; + +class MotorcycleFactory : public VehicleFactory { +public: + std::unique_ptr createVehicle() override { + std::cout << "🏭 Motorcycle Factory: Creating a new motorcycle..." << std::endl; + return std::make_unique(); + } +}; + +class TruckFactory : public VehicleFactory { +public: + std::unique_ptr createVehicle() override { + std::cout << "🏭 Truck Factory: Creating a new truck..." << std::endl; + return std::make_unique(); + } +}; + +class BicycleFactory : public VehicleFactory { +public: + std::unique_ptr createVehicle() override { + std::cout << "🏭 Bicycle Factory: Creating a new bicycle..." << std::endl; + return std::make_unique(); + } +}; + +int main() { + std::cout << "=== FACTORY METHOD PATTERN DEMO ===" << std::endl; + + // Array of different factories + std::vector> factories; + factories.push_back(std::make_unique()); + factories.push_back(std::make_unique()); + factories.push_back(std::make_unique()); + factories.push_back(std::make_unique()); + + // Process vehicles using different factories + for (auto& factory : factories) { + factory->processVehicle(); + } + + std::cout << "=== DIRECT VEHICLE CREATION ===" << std::endl; + // We can also create vehicles directly for comparison + auto directCar = std::make_unique(); + std::cout << "Direct creation: " << directCar->getType() << std::endl; + + std::cout << "\n=== FACTORY METHOD BENEFITS ===" << std::endl; + std::cout << "✓ Encapsulates object creation logic" << std::endl; + std::cout << "✓ Allows subclasses to decide which objects to create" << std::endl; + std::cout << "✓ Promotes loose coupling between creator and product" << std::endl; + std::cout << "✓ Follows Open/Closed Principle - easy to add new vehicle types" << std::endl; + std::cout << "✓ Client code doesn't need to know concrete classes" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/factory-method/solution.java b/src/content/design-pattern/code/factory-method/solution.java new file mode 100644 index 0000000..44c9e8d --- /dev/null +++ b/src/content/design-pattern/code/factory-method/solution.java @@ -0,0 +1,185 @@ +// Product interface +interface Vehicle { + void start(); + void stop(); + String getType(); + int getMaxSpeed(); +} + +// Concrete Products +class Car implements Vehicle { + @Override + public void start() { + System.out.println("🚗 Car engine starting..."); + } + + @Override + public void stop() { + System.out.println("🚗 Car engine stopping..."); + } + + @Override + public String getType() { + return "Car"; + } + + @Override + public int getMaxSpeed() { + return 200; // km/h + } +} + +class Motorcycle implements Vehicle { + @Override + public void start() { + System.out.println("🏍️ Motorcycle engine roaring to life..."); + } + + @Override + public void stop() { + System.out.println("🏍️ Motorcycle engine turning off..."); + } + + @Override + public String getType() { + return "Motorcycle"; + } + + @Override + public int getMaxSpeed() { + return 300; // km/h + } +} + +class Truck implements Vehicle { + @Override + public void start() { + System.out.println("🚚 Truck diesel engine starting..."); + } + + @Override + public void stop() { + System.out.println("🚚 Truck diesel engine stopping..."); + } + + @Override + public String getType() { + return "Truck"; + } + + @Override + public int getMaxSpeed() { + return 120; // km/h + } +} + +class Bicycle implements Vehicle { + @Override + public void start() { + System.out.println("🚲 Ready to pedal!"); + } + + @Override + public void stop() { + System.out.println("🚲 Stopped pedaling."); + } + + @Override + public String getType() { + return "Bicycle"; + } + + @Override + public int getMaxSpeed() { + return 50; // km/h + } +} + +// Creator abstract class +abstract class VehicleFactory { + // Factory method - to be implemented by subclasses + public abstract Vehicle createVehicle(); + + // Template method that uses the factory method + public void processVehicle() { + System.out.println("\n=== Processing Vehicle ==="); + Vehicle vehicle = createVehicle(); + + System.out.println("Vehicle Created: " + vehicle.getType()); + System.out.println("Max Speed: " + vehicle.getMaxSpeed() + " km/h"); + + vehicle.start(); + System.out.println("Vehicle is now operational!"); + + // Simulate usage + System.out.println("Using vehicle for transportation..."); + + vehicle.stop(); + System.out.println("Vehicle processing completed.\n"); + } +} + +// Concrete Creators +class CarFactory extends VehicleFactory { + @Override + public Vehicle createVehicle() { + System.out.println("🏭 Car Factory: Creating a new car..."); + return new Car(); + } +} + +class MotorcycleFactory extends VehicleFactory { + @Override + public Vehicle createVehicle() { + System.out.println("🏭 Motorcycle Factory: Creating a new motorcycle..."); + return new Motorcycle(); + } +} + +class TruckFactory extends VehicleFactory { + @Override + public Vehicle createVehicle() { + System.out.println("🏭 Truck Factory: Creating a new truck..."); + return new Truck(); + } +} + +class BicycleFactory extends VehicleFactory { + @Override + public Vehicle createVehicle() { + System.out.println("🏭 Bicycle Factory: Creating a new bicycle..."); + return new Bicycle(); + } +} + +// Client code +public class FactoryMethodDemo { + public static void main(String[] args) { + System.out.println("=== FACTORY METHOD PATTERN DEMO ==="); + + // Array of different factories + VehicleFactory[] factories = { + new CarFactory(), + new MotorcycleFactory(), + new TruckFactory(), + new BicycleFactory() + }; + + // Process vehicles using different factories + for (VehicleFactory factory : factories) { + factory.processVehicle(); + } + + System.out.println("=== DIRECT VEHICLE CREATION ==="); + // We can also create vehicles directly for comparison + Vehicle directCar = new Car(); + System.out.println("Direct creation: " + directCar.getType()); + + System.out.println("\n=== FACTORY METHOD BENEFITS ==="); + System.out.println("✓ Encapsulates object creation logic"); + System.out.println("✓ Allows subclasses to decide which objects to create"); + System.out.println("✓ Promotes loose coupling between creator and product"); + System.out.println("✓ Follows Open/Closed Principle - easy to add new vehicle types"); + System.out.println("✓ Client code doesn't need to know concrete classes"); + } +} diff --git a/src/content/design-pattern/code/factory-method/solution.py b/src/content/design-pattern/code/factory-method/solution.py new file mode 100644 index 0000000..d7e141b --- /dev/null +++ b/src/content/design-pattern/code/factory-method/solution.py @@ -0,0 +1,149 @@ +from abc import ABC, abstractmethod +from typing import List + +# Product interface +class Vehicle(ABC): + @abstractmethod + def start(self) -> None: + pass + + @abstractmethod + def stop(self) -> None: + pass + + @abstractmethod + def get_type(self) -> str: + pass + + @abstractmethod + def get_max_speed(self) -> int: + pass + +# Concrete Products +class Car(Vehicle): + def start(self) -> None: + print("🚗 Car engine starting...") + + def stop(self) -> None: + print("🚗 Car engine stopping...") + + def get_type(self) -> str: + return "Car" + + def get_max_speed(self) -> int: + return 200 # km/h + +class Motorcycle(Vehicle): + def start(self) -> None: + print("🏍️ Motorcycle engine roaring to life...") + + def stop(self) -> None: + print("🏍️ Motorcycle engine turning off...") + + def get_type(self) -> str: + return "Motorcycle" + + def get_max_speed(self) -> int: + return 300 # km/h + +class Truck(Vehicle): + def start(self) -> None: + print("🚚 Truck diesel engine starting...") + + def stop(self) -> None: + print("🚚 Truck diesel engine stopping...") + + def get_type(self) -> str: + return "Truck" + + def get_max_speed(self) -> int: + return 120 # km/h + +class Bicycle(Vehicle): + def start(self) -> None: + print("🚲 Ready to pedal!") + + def stop(self) -> None: + print("🚲 Stopped pedaling.") + + def get_type(self) -> str: + return "Bicycle" + + def get_max_speed(self) -> int: + return 50 # km/h + +# Creator abstract class +class VehicleFactory(ABC): + @abstractmethod + def create_vehicle(self) -> Vehicle: + """Factory method - to be implemented by subclasses""" + pass + + def process_vehicle(self) -> None: + """Template method that uses the factory method""" + print("\n=== Processing Vehicle ===") + vehicle = self.create_vehicle() + + print(f"Vehicle Created: {vehicle.get_type()}") + print(f"Max Speed: {vehicle.get_max_speed()} km/h") + + vehicle.start() + print("Vehicle is now operational!") + + # Simulate usage + print("Using vehicle for transportation...") + + vehicle.stop() + print("Vehicle processing completed.\n") + +# Concrete Creators +class CarFactory(VehicleFactory): + def create_vehicle(self) -> Vehicle: + print("🏭 Car Factory: Creating a new car...") + return Car() + +class MotorcycleFactory(VehicleFactory): + def create_vehicle(self) -> Vehicle: + print("🏭 Motorcycle Factory: Creating a new motorcycle...") + return Motorcycle() + +class TruckFactory(VehicleFactory): + def create_vehicle(self) -> Vehicle: + print("🏭 Truck Factory: Creating a new truck...") + return Truck() + +class BicycleFactory(VehicleFactory): + def create_vehicle(self) -> Vehicle: + print("🏭 Bicycle Factory: Creating a new bicycle...") + return Bicycle() + +# Client code +def main(): + print("=== FACTORY METHOD PATTERN DEMO ===") + + # Array of different factories + factories: List[VehicleFactory] = [ + CarFactory(), + MotorcycleFactory(), + TruckFactory(), + BicycleFactory() + ] + + # Process vehicles using different factories + for factory in factories: + factory.process_vehicle() + + print("=== DIRECT VEHICLE CREATION ===") + # We can also create vehicles directly for comparison + direct_car = Car() + print(f"Direct creation: {direct_car.get_type()}") + + print("\n=== FACTORY METHOD BENEFITS ===") + print("✓ Encapsulates object creation logic") + print("✓ Allows subclasses to decide which objects to create") + print("✓ Promotes loose coupling between creator and product") + print("✓ Follows Open/Closed Principle - easy to add new vehicle types") + print("✓ Client code doesn't need to know concrete classes") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/flyweight/solution.cpp b/src/content/design-pattern/code/flyweight/solution.cpp new file mode 100644 index 0000000..48c59a2 --- /dev/null +++ b/src/content/design-pattern/code/flyweight/solution.cpp @@ -0,0 +1,398 @@ +#include +#include +#include +#include +#include +#include +#include + +// Flyweight interface +class CharacterFlyweight { +public: + virtual ~CharacterFlyweight() = default; + virtual void display(int x, int y, const std::string& color, int fontSize) const = 0; +}; + +// Concrete Flyweight - stores intrinsic state (character and font family) +class CharacterType : public CharacterFlyweight { +private: + char character; + std::string fontFamily; + +public: + CharacterType(char ch, const std::string& font) : character(ch), fontFamily(font) {} + + void display(int x, int y, const std::string& color, int fontSize) const override { + std::cout << "Rendering '" << character << "' [" << fontFamily + << "] at (" << x << "," << y << ") in " << color + << " color, size " << fontSize << std::endl; + } + + char getCharacter() const { return character; } + const std::string& getFontFamily() const { return fontFamily; } +}; + +// Flyweight Factory - manages flyweight instances +class CharacterTypeFactory { +private: + static std::unordered_map> flyweights; + +public: + static std::shared_ptr getCharacterType(char character, const std::string& fontFamily) { + std::string key = std::string(1, character) + "_" + fontFamily; + + if (flyweights.find(key) == flyweights.end()) { + flyweights[key] = std::make_shared(character, fontFamily); + std::cout << "Creating new flyweight for: " << character << " (" << fontFamily << ")" << std::endl; + } + + return flyweights[key]; + } + + static int getCreatedFlyweightsCount() { + return flyweights.size(); + } + + static void printFlyweightStatistics() { + std::cout << "\n=== Flyweight Statistics ===" << std::endl; + std::cout << "Total flyweight instances created: " << flyweights.size() << std::endl; + std::cout << "Memory saved by sharing intrinsic state!" << std::endl; + } +}; + +// Initialize static member +std::unordered_map> CharacterTypeFactory::flyweights; + +// Context - stores extrinsic state and references to flyweights +class Character { +private: + std::shared_ptr flyweight; + int x, y; + std::string color; + int fontSize; + +public: + Character(char character, const std::string& fontFamily, int x, int y, + const std::string& color, int fontSize) + : flyweight(CharacterTypeFactory::getCharacterType(character, fontFamily)), + x(x), y(y), color(color), fontSize(fontSize) {} + + void display() const { + flyweight->display(x, y, color, fontSize); + } +}; + +// Document class that manages multiple characters +class Document { +private: + std::vector> characters; + +public: + void addCharacter(char character, const std::string& fontFamily, int x, int y, + const std::string& color, int fontSize) { + characters.push_back(std::make_unique(character, fontFamily, x, y, color, fontSize)); + } + + void render() const { + std::cout << "\n=== Rendering Document ===" << std::endl; + for (const auto& character : characters) { + character->display(); + } + } + + int getCharacterCount() const { + return characters.size(); + } +}; + +// Alternative example: Forest simulation with Tree flyweights +class TreeFlyweight { +public: + virtual ~TreeFlyweight() = default; + virtual void render(int x, int y, const std::string& climate) const = 0; +}; + +class TreeType : public TreeFlyweight { +private: + std::string name; + std::string color; + std::string texture; + +public: + TreeType(const std::string& name, const std::string& color, const std::string& texture) + : name(name), color(color), texture(texture) { + // Simulate loading heavy resources (textures, 3D models, etc.) + std::cout << "Loading heavy resources for tree type: " << name << std::endl; + } + + void render(int x, int y, const std::string& climate) const override { + std::cout << "Rendering " << name << " tree at (" << x << "," << y + << ") in " << climate << " climate [" << color + << " color, " << texture << " texture]" << std::endl; + } +}; + +class TreeTypeFactory { +private: + static std::unordered_map> treeTypes; + +public: + static std::shared_ptr getTreeType(const std::string& name, + const std::string& color, + const std::string& texture) { + std::string key = name + "_" + color + "_" + texture; + + if (treeTypes.find(key) == treeTypes.end()) { + treeTypes[key] = std::make_shared(name, color, texture); + } + + return treeTypes[key]; + } + + static int getTreeTypesCount() { + return treeTypes.size(); + } +}; + +// Initialize static member +std::unordered_map> TreeTypeFactory::treeTypes; + +class Tree { +private: + std::shared_ptr treeType; + int x, y; + std::string climate; + +public: + Tree(const std::string& name, const std::string& color, const std::string& texture, + int x, int y, const std::string& climate) + : treeType(TreeTypeFactory::getTreeType(name, color, texture)), + x(x), y(y), climate(climate) {} + + void render() const { + treeType->render(x, y, climate); + } +}; + +class Forest { +private: + std::vector> trees; + +public: + void plantTree(const std::string& name, const std::string& color, + const std::string& texture, int x, int y, const std::string& climate) { + trees.push_back(std::make_unique(name, color, texture, x, y, climate)); + } + + void render() const { + std::cout << "\n=== Rendering Forest ===" << std::endl; + for (const auto& tree : trees) { + tree->render(); + } + } + + int getTreeCount() const { + return trees.size(); + } +}; + +// Web Browser Font Example +class FontFlyweight { +private: + std::string family; + int size; + std::string style; + +public: + FontFlyweight(const std::string& family, int size, const std::string& style) + : family(family), size(size), style(style) { + // Simulate loading font file (expensive operation) + std::cout << "Loading font: " << family << "-" << size << "-" << style << std::endl; + } + + void renderText(const std::string& text, int x, int y, const std::string& color) const { + std::cout << "Rendering '" << text << "' with " << family << " " << size + << "pt " << style << " at (" << x << "," << y << ") in " << color << std::endl; + } +}; + +class FontFactory { +private: + static std::unordered_map> fonts; + +public: + static std::shared_ptr getFont(const std::string& family, int size, const std::string& style) { + std::ostringstream keyStream; + keyStream << family << "-" << size << "-" << style; + std::string key = keyStream.str(); + + if (fonts.find(key) == fonts.end()) { + fonts[key] = std::make_shared(family, size, style); + } + + return fonts[key]; + } + + static int getFontCount() { + return fonts.size(); + } +}; + +// Initialize static member +std::unordered_map> FontFactory::fonts; + +class TextElement { +private: + std::string text; + std::shared_ptr font; + int x, y; + std::string color; + +public: + TextElement(const std::string& text, const std::string& fontFamily, int fontSize, + const std::string& fontStyle, int x, int y, const std::string& color) + : text(text), font(FontFactory::getFont(fontFamily, fontSize, fontStyle)), + x(x), y(y), color(color) {} + + void render() const { + font->renderText(text, x, y, color); + } +}; + +class WebPage { +private: + std::vector> textElements; + +public: + void addText(const std::string& text, const std::string& fontFamily, int fontSize, + const std::string& fontStyle, int x, int y, const std::string& color) { + textElements.push_back(std::make_unique(text, fontFamily, fontSize, fontStyle, x, y, color)); + } + + void render() const { + std::cout << "\n=== Rendering Web Page ===" << std::endl; + for (const auto& element : textElements) { + element->render(); + } + } + + int getTextCount() const { + return textElements.size(); + } +}; + +void demoTextFormatting() { + std::cout << "=== Text Formatting System Demo ===\n" << std::endl; + + Document document; + + // Create a document with repeated characters + std::string text = "Hello World! This is a flyweight pattern demo."; + std::vector fonts = {"Arial", "Times New Roman", "Helvetica"}; + std::vector colors = {"black", "red", "blue"}; + std::vector fontSizes = {12, 14, 16}; + + int x = 0, y = 0; + for (char c : text) { + if (c != ' ') { + std::string font = fonts[x % fonts.size()]; + std::string color = colors[x % colors.size()]; + int fontSize = fontSizes[x % fontSizes.size()]; + + document.addCharacter(c, font, x * 10, y * 20, color, fontSize); + } + x++; + if (x > 40) { + x = 0; + y++; + } + } + + document.render(); + + std::cout << "\n=== Memory Efficiency Analysis ===" << std::endl; + std::cout << "Total characters in document: " << document.getCharacterCount() << std::endl; + CharacterTypeFactory::printFlyweightStatistics(); + + std::cout << "\nWithout Flyweight pattern:" << std::endl; + std::cout << "Memory usage would be: " << document.getCharacterCount() << " character objects" << std::endl; + std::cout << "With Flyweight pattern:" << std::endl; + std::cout << "Memory usage is: " << CharacterTypeFactory::getCreatedFlyweightsCount() + << " flyweight objects + " << document.getCharacterCount() << " context objects" << std::endl; + + double memorySaved = (double)(document.getCharacterCount() - CharacterTypeFactory::getCreatedFlyweightsCount()) / + document.getCharacterCount() * 100; + std::cout << "Memory efficiency: " << memorySaved << "% reduction in intrinsic state objects" << std::endl; +} + +void demoForestSimulation() { + std::cout << "\n\n=== Forest Simulation Demo ===\n" << std::endl; + + Forest forest; + std::mt19937 rng(42); // Fixed seed for reproducible results + + // Plant many trees with repeated types + std::vector treeNames = {"Oak", "Pine", "Birch", "Maple"}; + std::vector colors = {"Green", "Dark Green", "Light Green"}; + std::vector textures = {"Rough", "Smooth", "Textured"}; + std::vector climates = {"Temperate", "Cold", "Mild"}; + + std::cout << "Planting 20 trees...\n" << std::endl; + + for (int i = 0; i < 20; i++) { + std::string name = treeNames[rng() % treeNames.size()]; + std::string color = colors[rng() % colors.size()]; + std::string texture = textures[rng() % textures.size()]; + std::string climate = climates[rng() % climates.size()]; + + int x = rng() % 100; + int y = rng() % 100; + + forest.plantTree(name, color, texture, x, y, climate); + } + + forest.render(); + + std::cout << "\n=== Forest Memory Analysis ===" << std::endl; + std::cout << "Total trees in forest: " << forest.getTreeCount() << std::endl; + std::cout << "Tree type flyweights created: " << TreeTypeFactory::getTreeTypesCount() << std::endl; + + double treeMemorySaved = (double)(forest.getTreeCount() - TreeTypeFactory::getTreeTypesCount()) / + forest.getTreeCount() * 100; + std::cout << "Memory efficiency: " << treeMemorySaved << "% reduction in tree type objects" << std::endl; + std::cout << "\nEach tree type flyweight contains heavy resources (textures, 3D models)" << std::endl; + std::cout << "Sharing these flyweights saves significant memory!" << std::endl; +} + +void demoWebPage() { + std::cout << "\n\n=== Web Page Font Rendering Demo ===\n" << std::endl; + + WebPage webpage; + + // Add various text elements + webpage.addText("Welcome to Our Website", "Arial", 24, "bold", 10, 10, "black"); + webpage.addText("This is a subtitle", "Arial", 18, "normal", 10, 50, "gray"); + webpage.addText("Body text paragraph 1", "Times New Roman", 12, "normal", 10, 100, "black"); + webpage.addText("Body text paragraph 2", "Times New Roman", 12, "normal", 10, 130, "black"); + webpage.addText("Important Notice", "Arial", 14, "bold", 10, 180, "red"); + webpage.addText("Footer text", "Helvetica", 10, "italic", 10, 220, "gray"); + + // Add more elements with repeated fonts + for (int i = 0; i < 5; i++) { + webpage.addText("List item " + std::to_string(i + 1), "Arial", 12, "normal", 30, 250 + i * 20, "black"); + } + + webpage.render(); + + std::cout << "\n=== Font Flyweight Statistics ===" << std::endl; + std::cout << "Total text elements: " << webpage.getTextCount() << std::endl; + std::cout << "Font flyweights created: " << FontFactory::getFontCount() << std::endl; + std::cout << "Each font flyweight represents a loaded font file that can be reused!" << std::endl; +} + +int main() { + demoTextFormatting(); + demoForestSimulation(); + demoWebPage(); + return 0; +} diff --git a/src/content/design-pattern/code/flyweight/solution.java b/src/content/design-pattern/code/flyweight/solution.java new file mode 100644 index 0000000..5a41be2 --- /dev/null +++ b/src/content/design-pattern/code/flyweight/solution.java @@ -0,0 +1,263 @@ +import java.util.*; + +// Flyweight interface +interface CharacterFlyweight { + void display(int x, int y, String color, int fontSize); +} + +// Concrete Flyweight - stores intrinsic state (character and font family) +class CharacterType implements CharacterFlyweight { + private final char character; + private final String fontFamily; + + public CharacterType(char character, String fontFamily) { + this.character = character; + this.fontFamily = fontFamily; + } + + @Override + public void display(int x, int y, String color, int fontSize) { + // Simulate rendering character with extrinsic properties + System.out.printf("Rendering '%c' [%s] at (%d,%d) in %s color, size %d%n", + character, fontFamily, x, y, color, fontSize); + } + + public char getCharacter() { return character; } + public String getFontFamily() { return fontFamily; } +} + +// Flyweight Factory - manages flyweight instances +class CharacterTypeFactory { + private static final Map flyweights = new HashMap<>(); + + public static CharacterFlyweight getCharacterType(char character, String fontFamily) { + String key = character + "_" + fontFamily; + + if (!flyweights.containsKey(key)) { + flyweights.put(key, new CharacterType(character, fontFamily)); + System.out.println("Creating new flyweight for: " + character + " (" + fontFamily + ")"); + } + + return flyweights.get(key); + } + + public static int getCreatedFlyweightsCount() { + return flyweights.size(); + } + + public static void printFlyweightStatistics() { + System.out.println("\n=== Flyweight Statistics ==="); + System.out.println("Total flyweight instances created: " + flyweights.size()); + System.out.println("Memory saved by sharing intrinsic state!"); + } +} + +// Context - stores extrinsic state and references to flyweights +class Character { + private final CharacterFlyweight flyweight; + private final int x, y; + private final String color; + private final int fontSize; + + public Character(char character, String fontFamily, int x, int y, String color, int fontSize) { + this.flyweight = CharacterTypeFactory.getCharacterType(character, fontFamily); + this.x = x; + this.y = y; + this.color = color; + this.fontSize = fontSize; + } + + public void display() { + flyweight.display(x, y, color, fontSize); + } +} + +// Document class that manages multiple characters +class Document { + private final List characters = new ArrayList<>(); + + public void addCharacter(char character, String fontFamily, int x, int y, String color, int fontSize) { + characters.add(new Character(character, fontFamily, x, y, color, fontSize)); + } + + public void render() { + System.out.println("\n=== Rendering Document ==="); + for (Character character : characters) { + character.display(); + } + } + + public int getCharacterCount() { + return characters.size(); + } +} + +// Alternative example: Forest simulation with Tree flyweights +interface TreeFlyweight { + void render(int x, int y, String climate); +} + +class TreeType implements TreeFlyweight { + private final String name; + private final String color; + private final String texture; + + public TreeType(String name, String color, String texture) { + this.name = name; + this.color = color; + this.texture = texture; + // Simulate loading heavy resources (textures, 3D models, etc.) + System.out.println("Loading heavy resources for tree type: " + name); + } + + @Override + public void render(int x, int y, String climate) { + System.out.printf("Rendering %s tree at (%d,%d) in %s climate [%s color, %s texture]%n", + name, x, y, climate, color, texture); + } +} + +class TreeTypeFactory { + private static final Map treeTypes = new HashMap<>(); + + public static TreeFlyweight getTreeType(String name, String color, String texture) { + String key = name + "_" + color + "_" + texture; + + if (!treeTypes.containsKey(key)) { + treeTypes.put(key, new TreeType(name, color, texture)); + } + + return treeTypes.get(key); + } + + public static int getTreeTypesCount() { + return treeTypes.size(); + } +} + +class Tree { + private final TreeFlyweight treeType; + private final int x, y; + private final String climate; + + public Tree(String name, String color, String texture, int x, int y, String climate) { + this.treeType = TreeTypeFactory.getTreeType(name, color, texture); + this.x = x; + this.y = y; + this.climate = climate; + } + + public void render() { + treeType.render(x, y, climate); + } +} + +class Forest { + private final List trees = new ArrayList<>(); + + public void plantTree(String name, String color, String texture, int x, int y, String climate) { + trees.add(new Tree(name, color, texture, x, y, climate)); + } + + public void render() { + System.out.println("\n=== Rendering Forest ==="); + for (Tree tree : trees) { + tree.render(); + } + } + + public int getTreeCount() { + return trees.size(); + } +} + +// Client code demonstrating the Flyweight pattern +public class FlyweightPatternDemo { + public static void main(String[] args) { + demoTextFormatting(); + demoForestSimulation(); + } + + private static void demoTextFormatting() { + System.out.println("=== Text Formatting System Demo ===\n"); + + Document document = new Document(); + + // Create a document with repeated characters + String text = "Hello World! This is a flyweight pattern demo."; + String[] fonts = {"Arial", "Times New Roman", "Helvetica"}; + String[] colors = {"black", "red", "blue"}; + int[] fontSizes = {12, 14, 16}; + + int x = 0, y = 0; + for (char c : text.toCharArray()) { + if (c != ' ') { + String font = fonts[x % fonts.length]; + String color = colors[x % colors.length]; + int fontSize = fontSizes[x % fontSizes.length]; + + document.addCharacter(c, font, x * 10, y * 20, color, fontSize); + } + x++; + if (x > 40) { + x = 0; + y++; + } + } + + document.render(); + + System.out.println("\n=== Memory Efficiency Analysis ==="); + System.out.println("Total characters in document: " + document.getCharacterCount()); + CharacterTypeFactory.printFlyweightStatistics(); + + System.out.println("\nWithout Flyweight pattern:"); + System.out.println("Memory usage would be: " + document.getCharacterCount() + " character objects"); + System.out.println("With Flyweight pattern:"); + System.out.println("Memory usage is: " + CharacterTypeFactory.getCreatedFlyweightsCount() + " flyweight objects + " + + document.getCharacterCount() + " context objects"); + + double memorySaved = (double)(document.getCharacterCount() - CharacterTypeFactory.getCreatedFlyweightsCount()) / + document.getCharacterCount() * 100; + System.out.printf("Memory efficiency: %.1f%% reduction in intrinsic state objects%n", memorySaved); + } + + private static void demoForestSimulation() { + System.out.println("\n\n=== Forest Simulation Demo ===\n"); + + Forest forest = new Forest(); + Random random = new Random(42); // Fixed seed for reproducible results + + // Plant many trees with repeated types + String[] treeNames = {"Oak", "Pine", "Birch", "Maple"}; + String[] colors = {"Green", "Dark Green", "Light Green"}; + String[] textures = {"Rough", "Smooth", "Textured"}; + String[] climates = {"Temperate", "Cold", "Mild"}; + + System.out.println("Planting 20 trees...\n"); + + for (int i = 0; i < 20; i++) { + String name = treeNames[random.nextInt(treeNames.length)]; + String color = colors[random.nextInt(colors.length)]; + String texture = textures[random.nextInt(textures.length)]; + String climate = climates[random.nextInt(climates.length)]; + + int x = random.nextInt(100); + int y = random.nextInt(100); + + forest.plantTree(name, color, texture, x, y, climate); + } + + forest.render(); + + System.out.println("\n=== Forest Memory Analysis ==="); + System.out.println("Total trees in forest: " + forest.getTreeCount()); + System.out.println("Tree type flyweights created: " + TreeTypeFactory.getTreeTypesCount()); + + double treeMemorySaved = (double)(forest.getTreeCount() - TreeTypeFactory.getTreeTypesCount()) / + forest.getTreeCount() * 100; + System.out.printf("Memory efficiency: %.1f%% reduction in tree type objects%n", treeMemorySaved); + System.out.println("\nEach tree type flyweight contains heavy resources (textures, 3D models)"); + System.out.println("Sharing these flyweights saves significant memory!"); + } +} diff --git a/src/content/design-pattern/code/flyweight/solution.py b/src/content/design-pattern/code/flyweight/solution.py new file mode 100644 index 0000000..d85f687 --- /dev/null +++ b/src/content/design-pattern/code/flyweight/solution.py @@ -0,0 +1,337 @@ +from typing import Dict, List +import random + +class CharacterFlyweight: + """Flyweight interface for character rendering""" + + def display(self, x: int, y: int, color: str, font_size: int) -> None: + pass + + +class CharacterType(CharacterFlyweight): + """Concrete Flyweight - stores intrinsic state (character and font family)""" + + def __init__(self, character: str, font_family: str): + self._character = character + self._font_family = font_family + + def display(self, x: int, y: int, color: str, font_size: int) -> None: + """Render character with extrinsic properties""" + print(f"Rendering '{self._character}' [{self._font_family}] at ({x},{y}) in {color} color, size {font_size}") + + @property + def character(self) -> str: + return self._character + + @property + def font_family(self) -> str: + return self._font_family + + +class CharacterTypeFactory: + """Flyweight Factory - manages flyweight instances""" + + _flyweights: Dict[str, CharacterFlyweight] = {} + + @classmethod + def get_character_type(cls, character: str, font_family: str) -> CharacterFlyweight: + key = f"{character}_{font_family}" + + if key not in cls._flyweights: + cls._flyweights[key] = CharacterType(character, font_family) + print(f"Creating new flyweight for: {character} ({font_family})") + + return cls._flyweights[key] + + @classmethod + def get_created_flyweights_count(cls) -> int: + return len(cls._flyweights) + + @classmethod + def print_flyweight_statistics(cls) -> None: + print("\n=== Flyweight Statistics ===") + print(f"Total flyweight instances created: {len(cls._flyweights)}") + print("Memory saved by sharing intrinsic state!") + + +class Character: + """Context - stores extrinsic state and references to flyweights""" + + def __init__(self, character: str, font_family: str, x: int, y: int, color: str, font_size: int): + self._flyweight = CharacterTypeFactory.get_character_type(character, font_family) + self._x = x + self._y = y + self._color = color + self._font_size = font_size + + def display(self) -> None: + self._flyweight.display(self._x, self._y, self._color, self._font_size) + + +class Document: + """Document class that manages multiple characters""" + + def __init__(self): + self._characters: List[Character] = [] + + def add_character(self, character: str, font_family: str, x: int, y: int, color: str, font_size: int) -> None: + self._characters.append(Character(character, font_family, x, y, color, font_size)) + + def render(self) -> None: + print("\n=== Rendering Document ===") + for character in self._characters: + character.display() + + def get_character_count(self) -> int: + return len(self._characters) + + +# Alternative example: Forest simulation with Tree flyweights +class TreeFlyweight: + """Flyweight interface for tree rendering""" + + def render(self, x: int, y: int, climate: str) -> None: + pass + + +class TreeType(TreeFlyweight): + """Concrete flyweight for tree types""" + + def __init__(self, name: str, color: str, texture: str): + self._name = name + self._color = color + self._texture = texture + # Simulate loading heavy resources (textures, 3D models, etc.) + print(f"Loading heavy resources for tree type: {name}") + + def render(self, x: int, y: int, climate: str) -> None: + print(f"Rendering {self._name} tree at ({x},{y}) in {climate} climate [{self._color} color, {self._texture} texture]") + + +class TreeTypeFactory: + """Factory for managing tree type flyweights""" + + _tree_types: Dict[str, TreeFlyweight] = {} + + @classmethod + def get_tree_type(cls, name: str, color: str, texture: str) -> TreeFlyweight: + key = f"{name}_{color}_{texture}" + + if key not in cls._tree_types: + cls._tree_types[key] = TreeType(name, color, texture) + + return cls._tree_types[key] + + @classmethod + def get_tree_types_count(cls) -> int: + return len(cls._tree_types) + + +class Tree: + """Context class for individual trees""" + + def __init__(self, name: str, color: str, texture: str, x: int, y: int, climate: str): + self._tree_type = TreeTypeFactory.get_tree_type(name, color, texture) + self._x = x + self._y = y + self._climate = climate + + def render(self) -> None: + self._tree_type.render(self._x, self._y, self._climate) + + +class Forest: + """Forest that manages multiple trees""" + + def __init__(self): + self._trees: List[Tree] = [] + + def plant_tree(self, name: str, color: str, texture: str, x: int, y: int, climate: str) -> None: + self._trees.append(Tree(name, color, texture, x, y, climate)) + + def render(self) -> None: + print("\n=== Rendering Forest ===") + for tree in self._trees: + tree.render() + + def get_tree_count(self) -> int: + return len(self._trees) + + +# Web Browser Example: Sharing Font Objects +class FontFlyweight: + """Flyweight for font resources""" + + def __init__(self, family: str, size: int, style: str): + self.family = family + self.size = size + self.style = style + # Simulate loading font file (expensive operation) + print(f"Loading font: {family}-{size}-{style}") + + def render_text(self, text: str, x: int, y: int, color: str) -> None: + print(f"Rendering '{text}' with {self.family} {self.size}pt {self.style} at ({x},{y}) in {color}") + + +class FontFactory: + """Factory for managing font flyweights""" + + _fonts: Dict[str, FontFlyweight] = {} + + @classmethod + def get_font(cls, family: str, size: int, style: str) -> FontFlyweight: + key = f"{family}-{size}-{style}" + + if key not in cls._fonts: + cls._fonts[key] = FontFlyweight(family, size, style) + + return cls._fonts[key] + + @classmethod + def get_font_count(cls) -> int: + return len(cls._fonts) + + +class TextElement: + """Text element that uses font flyweight""" + + def __init__(self, text: str, font_family: str, font_size: int, font_style: str, + x: int, y: int, color: str): + self._text = text + self._font = FontFactory.get_font(font_family, font_size, font_style) + self._x = x + self._y = y + self._color = color + + def render(self) -> None: + self._font.render_text(self._text, self._x, self._y, self._color) + + +class WebPage: + """Web page containing multiple text elements""" + + def __init__(self): + self._text_elements: List[TextElement] = [] + + def add_text(self, text: str, font_family: str, font_size: int, font_style: str, + x: int, y: int, color: str) -> None: + self._text_elements.append(TextElement(text, font_family, font_size, font_style, x, y, color)) + + def render(self) -> None: + print("\n=== Rendering Web Page ===") + for element in self._text_elements: + element.render() + + def get_text_count(self) -> int: + return len(self._text_elements) + + +def demo_text_formatting(): + """Demonstrate text formatting system with flyweight pattern""" + print("=== Text Formatting System Demo ===\n") + + document = Document() + + # Create a document with repeated characters + text = "Hello World! This is a flyweight pattern demo." + fonts = ["Arial", "Times New Roman", "Helvetica"] + colors = ["black", "red", "blue"] + font_sizes = [12, 14, 16] + + x, y = 0, 0 + for char in text: + if char != ' ': + font = fonts[x % len(fonts)] + color = colors[x % len(colors)] + font_size = font_sizes[x % len(font_sizes)] + + document.add_character(char, font, x * 10, y * 20, color, font_size) + + x += 1 + if x > 40: + x = 0 + y += 1 + + document.render() + + print("\n=== Memory Efficiency Analysis ===") + print(f"Total characters in document: {document.get_character_count()}") + CharacterTypeFactory.print_flyweight_statistics() + + print("\nWithout Flyweight pattern:") + print(f"Memory usage would be: {document.get_character_count()} character objects") + print("With Flyweight pattern:") + print(f"Memory usage is: {CharacterTypeFactory.get_created_flyweights_count()} flyweight objects + {document.get_character_count()} context objects") + + memory_saved = (document.get_character_count() - CharacterTypeFactory.get_created_flyweights_count()) / document.get_character_count() * 100 + print(f"Memory efficiency: {memory_saved:.1f}% reduction in intrinsic state objects") + + +def demo_forest_simulation(): + """Demonstrate forest simulation with tree flyweights""" + print("\n\n=== Forest Simulation Demo ===\n") + + forest = Forest() + random.seed(42) # Fixed seed for reproducible results + + # Plant many trees with repeated types + tree_names = ["Oak", "Pine", "Birch", "Maple"] + colors = ["Green", "Dark Green", "Light Green"] + textures = ["Rough", "Smooth", "Textured"] + climates = ["Temperate", "Cold", "Mild"] + + print("Planting 20 trees...\n") + + for i in range(20): + name = random.choice(tree_names) + color = random.choice(colors) + texture = random.choice(textures) + climate = random.choice(climates) + + x = random.randint(0, 99) + y = random.randint(0, 99) + + forest.plant_tree(name, color, texture, x, y, climate) + + forest.render() + + print("\n=== Forest Memory Analysis ===") + print(f"Total trees in forest: {forest.get_tree_count()}") + print(f"Tree type flyweights created: {TreeTypeFactory.get_tree_types_count()}") + + tree_memory_saved = (forest.get_tree_count() - TreeTypeFactory.get_tree_types_count()) / forest.get_tree_count() * 100 + print(f"Memory efficiency: {tree_memory_saved:.1f}% reduction in tree type objects") + print("\nEach tree type flyweight contains heavy resources (textures, 3D models)") + print("Sharing these flyweights saves significant memory!") + + +def demo_web_page(): + """Demonstrate web page with font flyweights""" + print("\n\n=== Web Page Font Rendering Demo ===\n") + + webpage = WebPage() + + # Add various text elements + webpage.add_text("Welcome to Our Website", "Arial", 24, "bold", 10, 10, "black") + webpage.add_text("This is a subtitle", "Arial", 18, "normal", 10, 50, "gray") + webpage.add_text("Body text paragraph 1", "Times New Roman", 12, "normal", 10, 100, "black") + webpage.add_text("Body text paragraph 2", "Times New Roman", 12, "normal", 10, 130, "black") + webpage.add_text("Important Notice", "Arial", 14, "bold", 10, 180, "red") + webpage.add_text("Footer text", "Helvetica", 10, "italic", 10, 220, "gray") + + # Add more elements with repeated fonts + for i in range(5): + webpage.add_text(f"List item {i+1}", "Arial", 12, "normal", 30, 250 + i*20, "black") + + webpage.render() + + print(f"\n=== Font Flyweight Statistics ===") + print(f"Total text elements: {webpage.get_text_count()}") + print(f"Font flyweights created: {FontFactory.get_font_count()}") + print("Each font flyweight represents a loaded font file that can be reused!") + + +if __name__ == "__main__": + demo_text_formatting() + demo_forest_simulation() + demo_web_page() diff --git a/src/content/design-pattern/code/interpreter/solution.cpp b/src/content/design-pattern/code/interpreter/solution.cpp new file mode 100644 index 0000000..e6f5e9a --- /dev/null +++ b/src/content/design-pattern/code/interpreter/solution.cpp @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include + +// Context class to hold variable values +class Context { +private: + std::unordered_map variables; + +public: + void setVariable(const std::string& name, int value) { + variables[name] = value; + } + + int getVariable(const std::string& name) const { + auto it = variables.find(name); + return (it != variables.end()) ? it->second : 0; + } +}; + +// Abstract Expression +class Expression { +public: + virtual ~Expression() = default; + virtual int interpret(const Context& context) = 0; +}; + +// Terminal Expression for numbers +class NumberExpression : public Expression { +private: + int number; + +public: + explicit NumberExpression(int num) : number(num) {} + + int interpret(const Context& context) override { + return number; + } +}; + +// Terminal Expression for variables +class VariableExpression : public Expression { +private: + std::string variableName; + +public: + explicit VariableExpression(const std::string& name) : variableName(name) {} + + int interpret(const Context& context) override { + return context.getVariable(variableName); + } +}; + +// Non-terminal Expression for addition +class AddExpression : public Expression { +private: + std::unique_ptr leftExpression; + std::unique_ptr rightExpression; + +public: + AddExpression(std::unique_ptr left, std::unique_ptr right) + : leftExpression(std::move(left)), rightExpression(std::move(right)) {} + + int interpret(const Context& context) override { + return leftExpression->interpret(context) + rightExpression->interpret(context); + } +}; + +// Non-terminal Expression for subtraction +class SubtractExpression : public Expression { +private: + std::unique_ptr leftExpression; + std::unique_ptr rightExpression; + +public: + SubtractExpression(std::unique_ptr left, std::unique_ptr right) + : leftExpression(std::move(left)), rightExpression(std::move(right)) {} + + int interpret(const Context& context) override { + return leftExpression->interpret(context) - rightExpression->interpret(context); + } +}; + +// Non-terminal Expression for multiplication +class MultiplyExpression : public Expression { +private: + std::unique_ptr leftExpression; + std::unique_ptr rightExpression; + +public: + MultiplyExpression(std::unique_ptr left, std::unique_ptr right) + : leftExpression(std::move(left)), rightExpression(std::move(right)) {} + + int interpret(const Context& context) override { + return leftExpression->interpret(context) * rightExpression->interpret(context); + } +}; + +// Simple expression parser +class ExpressionParser { +public: + static std::unique_ptr parse(const std::string& expression) { + std::istringstream iss(expression); + std::vector tokens; + std::string token; + + while (iss >> token) { + tokens.push_back(token); + } + + if (tokens.size() == 3) { + auto left = parseOperand(tokens[0]); + const std::string& op = tokens[1]; + auto right = parseOperand(tokens[2]); + + if (op == "+") { + return std::make_unique(std::move(left), std::move(right)); + } else if (op == "-") { + return std::make_unique(std::move(left), std::move(right)); + } else if (op == "*") { + return std::make_unique(std::move(left), std::move(right)); + } + } + + return parseOperand(expression); + } + +private: + static std::unique_ptr parseOperand(const std::string& operand) { + try { + int number = std::stoi(operand); + return std::make_unique(number); + } catch (const std::invalid_argument&) { + return std::make_unique(operand); + } + } +}; + +// Client code +int main() { + Context context; + context.setVariable("x", 10); + context.setVariable("y", 5); + context.setVariable("z", 2); + + std::cout << "=== Interpreter Pattern Demo ===" << std::endl; + + // Direct expression building + auto expression1 = std::make_unique( + std::make_unique(10), + std::make_unique(5) + ); + std::cout << "10 + 5 = " << expression1->interpret(context) << std::endl; + + // Using variables + auto expression2 = std::make_unique( + std::make_unique("x"), + std::make_unique("y") + ); + std::cout << "x * y = " << expression2->interpret(context) << std::endl; + + // Complex expression + auto expression3 = std::make_unique( + std::make_unique("x"), + std::make_unique( + std::make_unique("y"), + std::make_unique("z") + ) + ); + std::cout << "x + (y * z) = " << expression3->interpret(context) << std::endl; + + // Using simple parser + std::cout << "\n=== Using Parser ===" << std::endl; + auto parsed1 = ExpressionParser::parse("x + y"); + std::cout << "x + y = " << parsed1->interpret(context) << std::endl; + + auto parsed2 = ExpressionParser::parse("10 * 3"); + std::cout << "10 * 3 = " << parsed2->interpret(context) << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/interpreter/solution.java b/src/content/design-pattern/code/interpreter/solution.java new file mode 100644 index 0000000..810b790 --- /dev/null +++ b/src/content/design-pattern/code/interpreter/solution.java @@ -0,0 +1,175 @@ +// Interpreter Design Pattern - Arithmetic Expression Evaluator +import java.util.*; + +// Context class to hold variable values +class Context { + private Map variables = new HashMap<>(); + + public void setVariable(String name, int value) { + variables.put(name, value); + } + + public int getVariable(String name) { + return variables.getOrDefault(name, 0); + } +} + +// Abstract Expression +abstract class Expression { + public abstract int interpret(Context context); +} + +// Terminal Expression for numbers +class NumberExpression extends Expression { + private int number; + + public NumberExpression(int number) { + this.number = number; + } + + @Override + public int interpret(Context context) { + return number; + } +} + +// Terminal Expression for variables +class VariableExpression extends Expression { + private String variableName; + + public VariableExpression(String variableName) { + this.variableName = variableName; + } + + @Override + public int interpret(Context context) { + return context.getVariable(variableName); + } +} + +// Non-terminal Expression for addition +class AddExpression extends Expression { + private Expression leftExpression; + private Expression rightExpression; + + public AddExpression(Expression left, Expression right) { + this.leftExpression = left; + this.rightExpression = right; + } + + @Override + public int interpret(Context context) { + return leftExpression.interpret(context) + rightExpression.interpret(context); + } +} + +// Non-terminal Expression for subtraction +class SubtractExpression extends Expression { + private Expression leftExpression; + private Expression rightExpression; + + public SubtractExpression(Expression left, Expression right) { + this.leftExpression = left; + this.rightExpression = right; + } + + @Override + public int interpret(Context context) { + return leftExpression.interpret(context) - rightExpression.interpret(context); + } +} + +// Non-terminal Expression for multiplication +class MultiplyExpression extends Expression { + private Expression leftExpression; + private Expression rightExpression; + + public MultiplyExpression(Expression left, Expression right) { + this.leftExpression = left; + this.rightExpression = right; + } + + @Override + public int interpret(Context context) { + return leftExpression.interpret(context) * rightExpression.interpret(context); + } +} + +// Simple expression parser +class ExpressionParser { + public static Expression parse(String expression, Context context) { + // Simple parsing for demonstration: "x + y * z" + // In real implementation, use proper parsing techniques + String[] tokens = expression.split(" "); + + if (tokens.length == 3) { + Expression left = parseOperand(tokens[0], context); + String operator = tokens[1]; + Expression right = parseOperand(tokens[2], context); + + switch (operator) { + case "+": + return new AddExpression(left, right); + case "-": + return new SubtractExpression(left, right); + case "*": + return new MultiplyExpression(left, right); + } + } + + return parseOperand(expression.trim(), context); + } + + private static Expression parseOperand(String operand, Context context) { + try { + int number = Integer.parseInt(operand); + return new NumberExpression(number); + } catch (NumberFormatException e) { + return new VariableExpression(operand); + } + } +} + +// Client code +public class Main { + public static void main(String[] args) { + Context context = new Context(); + context.setVariable("x", 10); + context.setVariable("y", 5); + context.setVariable("z", 2); + + System.out.println("=== Interpreter Pattern Demo ==="); + + // Direct expression building + Expression expression1 = new AddExpression( + new NumberExpression(10), + new NumberExpression(5) + ); + System.out.println("10 + 5 = " + expression1.interpret(context)); + + // Using variables + Expression expression2 = new MultiplyExpression( + new VariableExpression("x"), + new VariableExpression("y") + ); + System.out.println("x * y = " + expression2.interpret(context)); + + // Complex expression + Expression expression3 = new AddExpression( + new VariableExpression("x"), + new MultiplyExpression( + new VariableExpression("y"), + new VariableExpression("z") + ) + ); + System.out.println("x + (y * z) = " + expression3.interpret(context)); + + // Using simple parser + System.out.println("\n=== Using Parser ==="); + Expression parsed1 = ExpressionParser.parse("x + y", context); + System.out.println("x + y = " + parsed1.interpret(context)); + + Expression parsed2 = ExpressionParser.parse("10 * 3", context); + System.out.println("10 * 3 = " + parsed2.interpret(context)); + } +} diff --git a/src/content/design-pattern/code/interpreter/solution.py b/src/content/design-pattern/code/interpreter/solution.py new file mode 100644 index 0000000..c9fd6ef --- /dev/null +++ b/src/content/design-pattern/code/interpreter/solution.py @@ -0,0 +1,154 @@ +"""Interpreter Design Pattern - Arithmetic Expression Evaluator""" +from abc import ABC, abstractmethod +from typing import Dict + + +class Context: + """Context class to hold variable values""" + + def __init__(self): + self.variables: Dict[str, int] = {} + + def set_variable(self, name: str, value: int): + self.variables[name] = value + + def get_variable(self, name: str) -> int: + return self.variables.get(name, 0) + + +class Expression(ABC): + """Abstract Expression""" + + @abstractmethod + def interpret(self, context: Context) -> int: + pass + + +class NumberExpression(Expression): + """Terminal Expression for numbers""" + + def __init__(self, number: int): + self.number = number + + def interpret(self, context: Context) -> int: + return self.number + + +class VariableExpression(Expression): + """Terminal Expression for variables""" + + def __init__(self, variable_name: str): + self.variable_name = variable_name + + def interpret(self, context: Context) -> int: + return context.get_variable(self.variable_name) + + +class AddExpression(Expression): + """Non-terminal Expression for addition""" + + def __init__(self, left: Expression, right: Expression): + self.left_expression = left + self.right_expression = right + + def interpret(self, context: Context) -> int: + return self.left_expression.interpret(context) + self.right_expression.interpret(context) + + +class SubtractExpression(Expression): + """Non-terminal Expression for subtraction""" + + def __init__(self, left: Expression, right: Expression): + self.left_expression = left + self.right_expression = right + + def interpret(self, context: Context) -> int: + return self.left_expression.interpret(context) - self.right_expression.interpret(context) + + +class MultiplyExpression(Expression): + """Non-terminal Expression for multiplication""" + + def __init__(self, left: Expression, right: Expression): + self.left_expression = left + self.right_expression = right + + def interpret(self, context: Context) -> int: + return self.left_expression.interpret(context) * self.right_expression.interpret(context) + + +class ExpressionParser: + """Simple expression parser""" + + @staticmethod + def parse(expression: str, context: Context) -> Expression: + """Simple parsing for demonstration: 'x + y * z'""" + tokens = expression.split() + + if len(tokens) == 3: + left = ExpressionParser._parse_operand(tokens[0]) + operator = tokens[1] + right = ExpressionParser._parse_operand(tokens[2]) + + if operator == '+': + return AddExpression(left, right) + elif operator == '-': + return SubtractExpression(left, right) + elif operator == '*': + return MultiplyExpression(left, right) + + return ExpressionParser._parse_operand(expression.strip()) + + @staticmethod + def _parse_operand(operand: str) -> Expression: + try: + number = int(operand) + return NumberExpression(number) + except ValueError: + return VariableExpression(operand) + + +def main(): + """Client code""" + context = Context() + context.set_variable('x', 10) + context.set_variable('y', 5) + context.set_variable('z', 2) + + print("=== Interpreter Pattern Demo ===") + + # Direct expression building + expression1 = AddExpression( + NumberExpression(10), + NumberExpression(5) + ) + print(f"10 + 5 = {expression1.interpret(context)}") + + # Using variables + expression2 = MultiplyExpression( + VariableExpression('x'), + VariableExpression('y') + ) + print(f"x * y = {expression2.interpret(context)}") + + # Complex expression + expression3 = AddExpression( + VariableExpression('x'), + MultiplyExpression( + VariableExpression('y'), + VariableExpression('z') + ) + ) + print(f"x + (y * z) = {expression3.interpret(context)}") + + # Using simple parser + print("\n=== Using Parser ===") + parsed1 = ExpressionParser.parse('x + y', context) + print(f"x + y = {parsed1.interpret(context)}") + + parsed2 = ExpressionParser.parse('10 * 3', context) + print(f"10 * 3 = {parsed2.interpret(context)}") + + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/iterator/solution.cpp b/src/content/design-pattern/code/iterator/solution.cpp new file mode 100644 index 0000000..fc667e9 --- /dev/null +++ b/src/content/design-pattern/code/iterator/solution.cpp @@ -0,0 +1,406 @@ +// Iterator Pattern - Music Playlist System +// Provides sequential access to elements without exposing underlying representation + +#include +#include +#include +#include +#include +#include +#include + +// Song class - the elements we'll iterate over +class Song { +private: + std::string title; + std::string artist; + std::string album; + int duration; // in seconds + std::string genre; + +public: + Song(const std::string& t, const std::string& a, const std::string& al, int d, const std::string& g) + : title(t), artist(a), album(al), duration(d), genre(g) {} + + // Getters + std::string getTitle() const { return title; } + std::string getArtist() const { return artist; } + std::string getAlbum() const { return album; } + int getDuration() const { return duration; } + std::string getGenre() const { return genre; } + + std::string getFormattedDuration() const { + int minutes = duration / 60; + int seconds = duration % 60; + return std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds); + } + + std::string toString() const { + return "\"" + title + "\" by " + artist + " [" + album + "] (" + + getFormattedDuration() + ") - " + genre; + } + + bool operator==(const Song& other) const { + return title == other.title && artist == other.artist; + } +}; + +// Abstract Iterator interface +template +class Iterator { +public: + virtual ~Iterator() = default; + virtual bool hasNext() = 0; + virtual T next() = 0; + virtual void reset() = 0; +}; + +// Abstract Aggregate interface +template +class IterableCollection { +public: + virtual ~IterableCollection() = default; + virtual std::unique_ptr> createIterator() = 0; + virtual std::unique_ptr> createReverseIterator() = 0; + virtual std::unique_ptr> createShuffleIterator() = 0; +}; + +// Forward Iterator implementation +template +class ForwardIterator : public Iterator { +private: + std::vector collection; + size_t position; + +public: + ForwardIterator(const std::vector& coll) : collection(coll), position(0) {} + + bool hasNext() override { + return position < collection.size(); + } + + T next() override { + if (!hasNext()) { + throw std::runtime_error("No more elements"); + } + return collection[position++]; + } + + void reset() override { + position = 0; + } +}; + +// Reverse Iterator implementation +template +class ReverseIterator : public Iterator { +private: + std::vector collection; + int position; + +public: + ReverseIterator(const std::vector& coll) : collection(coll) { + position = static_cast(collection.size()) - 1; + } + + bool hasNext() override { + return position >= 0; + } + + T next() override { + if (!hasNext()) { + throw std::runtime_error("No more elements"); + } + return collection[position--]; + } + + void reset() override { + position = static_cast(collection.size()) - 1; + } +}; + +// Shuffle Iterator implementation +template +class ShuffleIterator : public Iterator { +private: + std::vector originalCollection; + std::vector shuffledCollection; + size_t position; + std::mt19937 rng; + +public: + ShuffleIterator(const std::vector& coll) + : originalCollection(coll), shuffledCollection(coll), position(0) { + // Initialize random number generator with current time + rng.seed(std::chrono::steady_clock::now().time_since_epoch().count()); + std::shuffle(shuffledCollection.begin(), shuffledCollection.end(), rng); + } + + bool hasNext() override { + return position < shuffledCollection.size(); + } + + T next() override { + if (!hasNext()) { + throw std::runtime_error("No more elements"); + } + return shuffledCollection[position++]; + } + + void reset() override { + shuffledCollection = originalCollection; + std::shuffle(shuffledCollection.begin(), shuffledCollection.end(), rng); + position = 0; + } +}; + +// Genre Filter Iterator - filters songs by genre +class GenreFilterIterator : public Iterator { +private: + std::vector collection; + std::string targetGenre; + size_t position; + + void findNextMatch() { + while (position < collection.size()) { + std::string songGenre = collection[position].getGenre(); + std::transform(songGenre.begin(), songGenre.end(), songGenre.begin(), ::tolower); + if (songGenre == targetGenre) { + break; + } + position++; + } + } + +public: + GenreFilterIterator(const std::vector& coll, const std::string& genre) + : collection(coll), targetGenre(genre), position(0) { + std::transform(targetGenre.begin(), targetGenre.end(), targetGenre.begin(), ::tolower); + findNextMatch(); + } + + bool hasNext() override { + return position < collection.size(); + } + + Song next() override { + if (!hasNext()) { + throw std::runtime_error("No more elements"); + } + Song result = collection[position++]; + findNextMatch(); + return result; + } + + void reset() override { + position = 0; + findNextMatch(); + } +}; + +// Concrete Aggregate - Music Playlist +class MusicPlaylist : public IterableCollection { +private: + std::vector songs; + std::string playlistName; + +public: + MusicPlaylist(const std::string& name) : playlistName(name) {} + + void addSong(const Song& song) { + auto it = std::find(songs.begin(), songs.end(), song); + if (it == songs.end()) { + songs.push_back(song); + std::cout << "Added to " << playlistName << ": " << song.getTitle() << std::endl; + } else { + std::cout << "Song already exists in playlist: " << song.getTitle() << std::endl; + } + } + + void removeSong(const Song& song) { + auto it = std::find(songs.begin(), songs.end(), song); + if (it != songs.end()) { + songs.erase(it); + std::cout << "Removed from " << playlistName << ": " << song.getTitle() << std::endl; + } else { + std::cout << "Song not found in playlist: " << song.getTitle() << std::endl; + } + } + + size_t size() const { + return songs.size(); + } + + std::string getName() const { + return playlistName; + } + + // Iterator factory methods + std::unique_ptr> createIterator() override { + return std::make_unique>(songs); + } + + std::unique_ptr> createReverseIterator() override { + return std::make_unique>(songs); + } + + std::unique_ptr> createShuffleIterator() override { + return std::make_unique>(songs); + } + + std::unique_ptr> createGenreIterator(const std::string& genre) { + return std::make_unique(songs, genre); + } + + void showPlaylistInfo() { + std::cout << "\n=== Playlist: " << playlistName << " ===" << std::endl; + std::cout << "Total songs: " << songs.size() << std::endl; + if (!songs.empty()) { + int totalDuration = 0; + for (const auto& song : songs) { + totalDuration += song.getDuration(); + } + std::cout << "Total duration: " << formatTotalDuration(totalDuration) << std::endl; + } + } + +private: + std::string formatTotalDuration(int totalSeconds) { + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + + if (hours > 0) { + return std::to_string(hours) + ":" + + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + + (seconds < 10 ? "0" : "") + std::to_string(seconds); + } else { + return std::to_string(minutes) + ":" + + (seconds < 10 ? "0" : "") + std::to_string(seconds); + } + } +}; + +// Music Player using Iterator +class MusicPlayer { +private: + std::string playerName; + +public: + MusicPlayer(const std::string& name) : playerName(name) {} + + void play(std::unique_ptr> iterator, const std::string& mode) { + std::cout << "\n" << playerName << " - Playing in " << mode << " mode:" << std::endl; + std::cout << std::string(playerName.length() + mode.length() + 20, '=') << std::endl; + + int trackNumber = 1; + while (iterator->hasNext()) { + Song song = iterator->next(); + std::cout << trackNumber << ". " << song.toString() << std::endl; + trackNumber++; + } + + if (trackNumber == 1) { + std::cout << "No songs found for the specified criteria." << std::endl; + } + std::cout << std::endl; + } + + void playFirst(std::unique_ptr> iterator, int count, const std::string& mode) { + std::cout << "\n" << playerName << " - Playing first " << count << " songs in " << mode << " mode:" << std::endl; + std::cout << std::string(playerName.length() + mode.length() + 30, '=') << std::endl; + + int trackNumber = 1; + while (iterator->hasNext() && trackNumber <= count) { + Song song = iterator->next(); + std::cout << trackNumber << ". " << song.toString() << std::endl; + trackNumber++; + } + std::cout << std::endl; + } +}; + +// Main demonstration +int main() { + std::cout << "=== Iterator Pattern - Music Playlist System ===\n" << std::endl; + + // Create songs + std::vector songsToAdd = { + Song("Bohemian Rhapsody", "Queen", "A Night at the Opera", 355, "Rock"), + Song("Stairway to Heaven", "Led Zeppelin", "Led Zeppelin IV", 482, "Rock"), + Song("Hotel California", "Eagles", "Hotel California", 391, "Rock"), + Song("Imagine", "John Lennon", "Imagine", 183, "Pop"), + Song("Yesterday", "The Beatles", "Help!", 125, "Pop"), + Song("What's Going On", "Marvin Gaye", "What's Going On", 229, "Soul"), + Song("Respect", "Aretha Franklin", "I Never Loved a Man", 147, "Soul"), + Song("Like a Rolling Stone", "Bob Dylan", "Highway 61 Revisited", 369, "Folk"), + Song("Smells Like Teen Spirit", "Nirvana", "Nevermind", 301, "Grunge"), + Song("Billie Jean", "Michael Jackson", "Thriller", 294, "Pop") + }; + + // Create playlist and add songs + MusicPlaylist myPlaylist("My Greatest Hits"); + + for (const auto& song : songsToAdd) { + myPlaylist.addSong(song); + } + + myPlaylist.showPlaylistInfo(); + + // Create music player + MusicPlayer player("Spotify Player"); + + // Demonstrate different iteration patterns + std::cout << "\n1. Forward Iteration:" << std::endl; + player.play(myPlaylist.createIterator(), "Sequential"); + + std::cout << "2. Reverse Iteration:" << std::endl; + player.play(myPlaylist.createReverseIterator(), "Reverse"); + + std::cout << "3. Shuffle Iteration:" << std::endl; + player.play(myPlaylist.createShuffleIterator(), "Shuffle"); + + std::cout << "4. Genre Filter Iteration (Rock songs only):" << std::endl; + player.play(myPlaylist.createGenreIterator("Rock"), "Rock Filter"); + + std::cout << "5. Genre Filter Iteration (Pop songs only):" << std::endl; + player.play(myPlaylist.createGenreIterator("Pop"), "Pop Filter"); + + std::cout << "6. Limited Playback (First 3 songs):" << std::endl; + player.playFirst(myPlaylist.createIterator(), 3, "Sequential"); + + // Demonstrate iterator reset functionality + std::cout << "7. Iterator Reset Demonstration:" << std::endl; + auto resetIterator = myPlaylist.createShuffleIterator(); + std::cout << "First shuffle:" << std::endl; + player.playFirst(std::move(resetIterator), 3, "Shuffle"); + + resetIterator = myPlaylist.createShuffleIterator(); + resetIterator->reset(); + std::cout << "After reset - Second shuffle:" << std::endl; + player.playFirst(std::move(resetIterator), 3, "Shuffle"); + + // Demonstrate multiple simultaneous iterators + std::cout << "8. Multiple Simultaneous Iterators:" << std::endl; + auto iter1 = myPlaylist.createIterator(); + auto iter2 = myPlaylist.createReverseIterator(); + + std::cout << "Forward iterator - First song: " << + (iter1->hasNext() ? iter1->next().getTitle() : "None") << std::endl; + std::cout << "Reverse iterator - First song: " << + (iter2->hasNext() ? iter2->next().getTitle() : "None") << std::endl; + std::cout << "Forward iterator - Second song: " << + (iter1->hasNext() ? iter1->next().getTitle() : "None") << std::endl; + std::cout << "Reverse iterator - Second song: " << + (iter2->hasNext() ? iter2->next().getTitle() : "None") << std::endl; + + std::cout << "\n=== Iterator Pattern Benefits ===" << std::endl; + std::cout << "1. Uniform Interface: Same interface for different traversal algorithms" << std::endl; + std::cout << "2. Encapsulation: Internal structure of collection is hidden" << std::endl; + std::cout << "3. Multiple Iterators: Can have multiple iterators on same collection" << std::endl; + std::cout << "4. Polymorphic Iteration: Client code works with any iterator implementation" << std::endl; + std::cout << "5. Lazy Evaluation: Elements are accessed only when needed" << std::endl; + std::cout << "6. Memory Efficient: Don't need to load all elements at once" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/iterator/solution.java b/src/content/design-pattern/code/iterator/solution.java new file mode 100644 index 0000000..08ce35b --- /dev/null +++ b/src/content/design-pattern/code/iterator/solution.java @@ -0,0 +1,400 @@ +// Iterator Pattern - Music Playlist System +// Provides sequential access to elements without exposing underlying representation + +import java.util.*; + +// Song class - the elements we'll iterate over +class Song { + private String title; + private String artist; + private String album; + private int duration; // in seconds + private String genre; + + public Song(String title, String artist, String album, int duration, String genre) { + this.title = title; + this.artist = artist; + this.album = album; + this.duration = duration; + this.genre = genre; + } + + // Getters + public String getTitle() { return title; } + public String getArtist() { return artist; } + public String getAlbum() { return album; } + public int getDuration() { return duration; } + public String getGenre() { return genre; } + + public String getFormattedDuration() { + int minutes = duration / 60; + int seconds = duration % 60; + return String.format("%d:%02d", minutes, seconds); + } + + @Override + public String toString() { + return String.format("\"%s\" by %s [%s] (%s) - %s", + title, artist, album, getFormattedDuration(), genre); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Song song = (Song) obj; + return Objects.equals(title, song.title) && Objects.equals(artist, song.artist); + } + + @Override + public int hashCode() { + return Objects.hash(title, artist); + } +} + +// Generic Iterator interface +interface Iterator { + boolean hasNext(); + T next(); + void reset(); +} + +// Aggregate interface +interface IterableCollection { + Iterator createIterator(); + Iterator createReverseIterator(); + Iterator createShuffleIterator(); +} + +// Forward Iterator implementation +class ForwardIterator implements Iterator { + private List collection; + private int position; + + public ForwardIterator(List collection) { + this.collection = new ArrayList<>(collection); + this.position = 0; + } + + @Override + public boolean hasNext() { + return position < collection.size(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + return collection.get(position++); + } + + @Override + public void reset() { + position = 0; + } +} + +// Reverse Iterator implementation +class ReverseIterator implements Iterator { + private List collection; + private int position; + + public ReverseIterator(List collection) { + this.collection = new ArrayList<>(collection); + this.position = collection.size() - 1; + } + + @Override + public boolean hasNext() { + return position >= 0; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + return collection.get(position--); + } + + @Override + public void reset() { + position = collection.size() - 1; + } +} + +// Shuffle Iterator implementation +class ShuffleIterator implements Iterator { + private List shuffledCollection; + private int position; + + public ShuffleIterator(List collection) { + this.shuffledCollection = new ArrayList<>(collection); + Collections.shuffle(this.shuffledCollection); + this.position = 0; + } + + @Override + public boolean hasNext() { + return position < shuffledCollection.size(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + return shuffledCollection.get(position++); + } + + @Override + public void reset() { + Collections.shuffle(shuffledCollection); + position = 0; + } +} + +// Genre Filter Iterator - filters songs by genre +class GenreFilterIterator implements Iterator { + private List collection; + private String targetGenre; + private int position; + + public GenreFilterIterator(List collection, String genre) { + this.collection = new ArrayList<>(collection); + this.targetGenre = genre.toLowerCase(); + this.position = 0; + findNextMatch(); + } + + @Override + public boolean hasNext() { + return position < collection.size(); + } + + @Override + public Song next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + Song result = collection.get(position++); + findNextMatch(); + return result; + } + + @Override + public void reset() { + position = 0; + findNextMatch(); + } + + private void findNextMatch() { + while (position < collection.size() && + !collection.get(position).getGenre().toLowerCase().equals(targetGenre)) { + position++; + } + } +} + +// Concrete Aggregate - Music Playlist +class MusicPlaylist implements IterableCollection { + private List songs; + private String playlistName; + + public MusicPlaylist(String name) { + this.playlistName = name; + this.songs = new ArrayList<>(); + } + + public void addSong(Song song) { + if (!songs.contains(song)) { + songs.add(song); + System.out.println("Added to " + playlistName + ": " + song.getTitle()); + } else { + System.out.println("Song already exists in playlist: " + song.getTitle()); + } + } + + public void removeSong(Song song) { + if (songs.remove(song)) { + System.out.println("Removed from " + playlistName + ": " + song.getTitle()); + } else { + System.out.println("Song not found in playlist: " + song.getTitle()); + } + } + + public int size() { + return songs.size(); + } + + public String getName() { + return playlistName; + } + + // Iterator factory methods + @Override + public Iterator createIterator() { + return new ForwardIterator<>(songs); + } + + @Override + public Iterator createReverseIterator() { + return new ReverseIterator<>(songs); + } + + @Override + public Iterator createShuffleIterator() { + return new ShuffleIterator<>(songs); + } + + public Iterator createGenreIterator(String genre) { + return new GenreFilterIterator(songs, genre); + } + + // Utility method to display playlist info + public void showPlaylistInfo() { + System.out.println("\n=== Playlist: " + playlistName + " ==="); + System.out.println("Total songs: " + songs.size()); + if (!songs.isEmpty()) { + int totalDuration = songs.stream().mapToInt(Song::getDuration).sum(); + System.out.println("Total duration: " + formatTotalDuration(totalDuration)); + } + } + + private String formatTotalDuration(int totalSeconds) { + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + if (hours > 0) { + return String.format("%d:%02d:%02d", hours, minutes, seconds); + } else { + return String.format("%d:%02d", minutes, seconds); + } + } +} + +// Music Player using Iterator +class MusicPlayer { + private String playerName; + + public MusicPlayer(String name) { + this.playerName = name; + } + + public void play(Iterator iterator, String mode) { + System.out.println("\n" + playerName + " - Playing in " + mode + " mode:"); + System.out.println("=" + "=".repeat(playerName.length() + mode.length() + 20)); + + int trackNumber = 1; + while (iterator.hasNext()) { + Song song = iterator.next(); + System.out.println(trackNumber + ". " + song); + trackNumber++; + } + + if (trackNumber == 1) { + System.out.println("No songs found for the specified criteria."); + } + System.out.println(); + } + + public void playFirst(Iterator iterator, int count, String mode) { + System.out.println("\n" + playerName + " - Playing first " + count + " songs in " + mode + " mode:"); + System.out.println("=" + "=".repeat(playerName.length() + mode.length() + 30)); + + int trackNumber = 1; + while (iterator.hasNext() && trackNumber <= count) { + Song song = iterator.next(); + System.out.println(trackNumber + ". " + song); + trackNumber++; + } + System.out.println(); + } +} + +// Main demonstration class +public class IteratorPatternDemo { + public static void main(String[] args) { + System.out.println("=== Iterator Pattern - Music Playlist System ===\n"); + + // Create songs + List songsToAdd = Arrays.asList( + new Song("Bohemian Rhapsody", "Queen", "A Night at the Opera", 355, "Rock"), + new Song("Stairway to Heaven", "Led Zeppelin", "Led Zeppelin IV", 482, "Rock"), + new Song("Hotel California", "Eagles", "Hotel California", 391, "Rock"), + new Song("Imagine", "John Lennon", "Imagine", 183, "Pop"), + new Song("Yesterday", "The Beatles", "Help!", 125, "Pop"), + new Song("What's Going On", "Marvin Gaye", "What's Going On", 229, "Soul"), + new Song("Respect", "Aretha Franklin", "I Never Loved a Man", 147, "Soul"), + new Song("Like a Rolling Stone", "Bob Dylan", "Highway 61 Revisited", 369, "Folk"), + new Song("Smells Like Teen Spirit", "Nirvana", "Nevermind", 301, "Grunge"), + new Song("Billie Jean", "Michael Jackson", "Thriller", 294, "Pop") + ); + + // Create playlist and add songs + MusicPlaylist myPlaylist = new MusicPlaylist("My Greatest Hits"); + + for (Song song : songsToAdd) { + myPlaylist.addSong(song); + } + + myPlaylist.showPlaylistInfo(); + + // Create music player + MusicPlayer player = new MusicPlayer("Spotify Player"); + + // Demonstrate different iteration patterns + System.out.println("\n1. Forward Iteration:"); + Iterator forwardIterator = myPlaylist.createIterator(); + player.play(forwardIterator, "Sequential"); + + System.out.println("2. Reverse Iteration:"); + Iterator reverseIterator = myPlaylist.createReverseIterator(); + player.play(reverseIterator, "Reverse"); + + System.out.println("3. Shuffle Iteration:"); + Iterator shuffleIterator = myPlaylist.createShuffleIterator(); + player.play(shuffleIterator, "Shuffle"); + + System.out.println("4. Genre Filter Iteration (Rock songs only):"); + Iterator rockIterator = myPlaylist.createGenreIterator("Rock"); + player.play(rockIterator, "Rock Filter"); + + System.out.println("5. Genre Filter Iteration (Pop songs only):"); + Iterator popIterator = myPlaylist.createGenreIterator("Pop"); + player.play(popIterator, "Pop Filter"); + + System.out.println("6. Limited Playback (First 3 songs):"); + Iterator limitedIterator = myPlaylist.createIterator(); + player.playFirst(limitedIterator, 3, "Sequential"); + + // Demonstrate iterator reset functionality + System.out.println("7. Iterator Reset Demonstration:"); + Iterator resetIterator = myPlaylist.createShuffleIterator(); + System.out.println("First shuffle:"); + player.playFirst(resetIterator, 3, "Shuffle"); + + resetIterator.reset(); + System.out.println("After reset - Second shuffle:"); + player.playFirst(resetIterator, 3, "Shuffle"); + + System.out.println("\n=== Iterator Pattern Benefits ==="); + System.out.println("1. Uniform Interface: Same interface for different traversal algorithms"); + System.out.println("2. Encapsulation: Internal structure of collection is hidden"); + System.out.println("3. Multiple Iterators: Can have multiple iterators on same collection"); + System.out.println("4. Polymorphic Iteration: Client code works with any iterator implementation"); + System.out.println("5. Lazy Evaluation: Elements are accessed only when needed"); + System.out.println("6. Memory Efficient: Don't need to load all elements at once"); + + // Demonstrate multiple simultaneous iterators + System.out.println("\n8. Multiple Simultaneous Iterators:"); + Iterator iter1 = myPlaylist.createIterator(); + Iterator iter2 = myPlaylist.createReverseIterator(); + + System.out.println("Forward iterator - First song: " + (iter1.hasNext() ? iter1.next().getTitle() : "None")); + System.out.println("Reverse iterator - First song: " + (iter2.hasNext() ? iter2.next().getTitle() : "None")); + System.out.println("Forward iterator - Second song: " + (iter1.hasNext() ? iter1.next().getTitle() : "None")); + System.out.println("Reverse iterator - Second song: " + (iter2.hasNext() ? iter2.next().getTitle() : "None")); + } +} diff --git a/src/content/design-pattern/code/iterator/solution.py b/src/content/design-pattern/code/iterator/solution.py new file mode 100644 index 0000000..29fb148 --- /dev/null +++ b/src/content/design-pattern/code/iterator/solution.py @@ -0,0 +1,375 @@ +""" +Iterator Pattern - Music Playlist System +Provides sequential access to elements without exposing underlying representation +""" + +from abc import ABC, abstractmethod +from typing import List, Optional, TypeVar, Generic +import random + +T = TypeVar('T') + +class Song: + """Represents a song with metadata""" + + def __init__(self, title: str, artist: str, album: str, duration: int, genre: str): + self.title = title + self.artist = artist + self.album = album + self.duration = duration # in seconds + self.genre = genre + + def get_formatted_duration(self) -> str: + """Get duration in mm:ss format""" + minutes = self.duration // 60 + seconds = self.duration % 60 + return f"{minutes}:{seconds:02d}" + + def __str__(self) -> str: + return f'"{self.title}" by {self.artist} [{self.album}] ({self.get_formatted_duration()}) - {self.genre}' + + def __eq__(self, other) -> bool: + if not isinstance(other, Song): + return False + return self.title == other.title and self.artist == other.artist + + def __hash__(self) -> int: + return hash((self.title, self.artist)) + +class Iterator(ABC, Generic[T]): + """Abstract iterator interface""" + + @abstractmethod + def has_next(self) -> bool: + """Check if there are more elements""" + pass + + @abstractmethod + def next(self) -> T: + """Get the next element""" + pass + + @abstractmethod + def reset(self) -> None: + """Reset iterator to beginning""" + pass + +class IterableCollection(ABC, Generic[T]): + """Abstract aggregate interface""" + + @abstractmethod + def create_iterator(self) -> Iterator[T]: + """Create forward iterator""" + pass + + @abstractmethod + def create_reverse_iterator(self) -> Iterator[T]: + """Create reverse iterator""" + pass + + @abstractmethod + def create_shuffle_iterator(self) -> Iterator[T]: + """Create shuffle iterator""" + pass + +class ForwardIterator(Iterator[T]): + """Iterator that traverses collection from beginning to end""" + + def __init__(self, collection: List[T]): + self._collection = collection.copy() + self._position = 0 + + def has_next(self) -> bool: + return self._position < len(self._collection) + + def next(self) -> T: + if not self.has_next(): + raise StopIteration("No more elements") + result = self._collection[self._position] + self._position += 1 + return result + + def reset(self) -> None: + self._position = 0 + +class ReverseIterator(Iterator[T]): + """Iterator that traverses collection from end to beginning""" + + def __init__(self, collection: List[T]): + self._collection = collection.copy() + self._position = len(self._collection) - 1 + + def has_next(self) -> bool: + return self._position >= 0 + + def next(self) -> T: + if not self.has_next(): + raise StopIteration("No more elements") + result = self._collection[self._position] + self._position -= 1 + return result + + def reset(self) -> None: + self._position = len(self._collection) - 1 + +class ShuffleIterator(Iterator[T]): + """Iterator that traverses collection in random order""" + + def __init__(self, collection: List[T]): + self._original_collection = collection.copy() + self._shuffled_collection = collection.copy() + random.shuffle(self._shuffled_collection) + self._position = 0 + + def has_next(self) -> bool: + return self._position < len(self._shuffled_collection) + + def next(self) -> T: + if not self.has_next(): + raise StopIteration("No more elements") + result = self._shuffled_collection[self._position] + self._position += 1 + return result + + def reset(self) -> None: + """Reset and reshuffle""" + self._shuffled_collection = self._original_collection.copy() + random.shuffle(self._shuffled_collection) + self._position = 0 + +class GenreFilterIterator(Iterator[Song]): + """Iterator that filters songs by genre""" + + def __init__(self, collection: List[Song], genre: str): + self._collection = collection.copy() + self._target_genre = genre.lower() + self._position = 0 + self._find_next_match() + + def has_next(self) -> bool: + return self._position < len(self._collection) + + def next(self) -> Song: + if not self.has_next(): + raise StopIteration("No more elements") + result = self._collection[self._position] + self._position += 1 + self._find_next_match() + return result + + def reset(self) -> None: + self._position = 0 + self._find_next_match() + + def _find_next_match(self) -> None: + """Find next song that matches the target genre""" + while (self._position < len(self._collection) and + self._collection[self._position].genre.lower() != self._target_genre): + self._position += 1 + +class MusicPlaylist(IterableCollection[Song]): + """Concrete aggregate - Music Playlist""" + + def __init__(self, name: str): + self._playlist_name = name + self._songs: List[Song] = [] + + def add_song(self, song: Song) -> None: + """Add a song to the playlist""" + if song not in self._songs: + self._songs.append(song) + print(f"Added to {self._playlist_name}: {song.title}") + else: + print(f"Song already exists in playlist: {song.title}") + + def remove_song(self, song: Song) -> None: + """Remove a song from the playlist""" + if song in self._songs: + self._songs.remove(song) + print(f"Removed from {self._playlist_name}: {song.title}") + else: + print(f"Song not found in playlist: {song.title}") + + def size(self) -> int: + """Get number of songs in playlist""" + return len(self._songs) + + @property + def name(self) -> str: + """Get playlist name""" + return self._playlist_name + + # Iterator factory methods + def create_iterator(self) -> Iterator[Song]: + return ForwardIterator(self._songs) + + def create_reverse_iterator(self) -> Iterator[Song]: + return ReverseIterator(self._songs) + + def create_shuffle_iterator(self) -> Iterator[Song]: + return ShuffleIterator(self._songs) + + def create_genre_iterator(self, genre: str) -> Iterator[Song]: + """Create iterator that filters by genre""" + return GenreFilterIterator(self._songs, genre) + + def show_playlist_info(self) -> None: + """Display playlist information""" + print(f"\n=== Playlist: {self._playlist_name} ===") + print(f"Total songs: {len(self._songs)}") + if self._songs: + total_duration = sum(song.duration for song in self._songs) + print(f"Total duration: {self._format_total_duration(total_duration)}") + + def _format_total_duration(self, total_seconds: int) -> str: + """Format total duration as h:mm:ss or mm:ss""" + hours = total_seconds // 3600 + minutes = (total_seconds % 3600) // 60 + seconds = total_seconds % 60 + + if hours > 0: + return f"{hours}:{minutes:02d}:{seconds:02d}" + else: + return f"{minutes}:{seconds:02d}" + +class MusicPlayer: + """Music player that uses iterators to play songs""" + + def __init__(self, name: str): + self._player_name = name + + def play(self, iterator: Iterator[Song], mode: str) -> None: + """Play all songs using the provided iterator""" + print(f"\n{self._player_name} - Playing in {mode} mode:") + print("=" * (len(self._player_name) + len(mode) + 20)) + + track_number = 1 + while iterator.has_next(): + song = iterator.next() + print(f"{track_number}. {song}") + track_number += 1 + + if track_number == 1: + print("No songs found for the specified criteria.") + print() + + def play_first(self, iterator: Iterator[Song], count: int, mode: str) -> None: + """Play first N songs using the provided iterator""" + print(f"\n{self._player_name} - Playing first {count} songs in {mode} mode:") + print("=" * (len(self._player_name) + len(mode) + 30)) + + track_number = 1 + while iterator.has_next() and track_number <= count: + song = iterator.next() + print(f"{track_number}. {song}") + track_number += 1 + print() + +def main(): + """Demonstrate the Iterator pattern""" + print("=== Iterator Pattern - Music Playlist System ===\n") + + # Create songs + songs_to_add = [ + Song("Bohemian Rhapsody", "Queen", "A Night at the Opera", 355, "Rock"), + Song("Stairway to Heaven", "Led Zeppelin", "Led Zeppelin IV", 482, "Rock"), + Song("Hotel California", "Eagles", "Hotel California", 391, "Rock"), + Song("Imagine", "John Lennon", "Imagine", 183, "Pop"), + Song("Yesterday", "The Beatles", "Help!", 125, "Pop"), + Song("What's Going On", "Marvin Gaye", "What's Going On", 229, "Soul"), + Song("Respect", "Aretha Franklin", "I Never Loved a Man", 147, "Soul"), + Song("Like a Rolling Stone", "Bob Dylan", "Highway 61 Revisited", 369, "Folk"), + Song("Smells Like Teen Spirit", "Nirvana", "Nevermind", 301, "Grunge"), + Song("Billie Jean", "Michael Jackson", "Thriller", 294, "Pop") + ] + + # Create playlist and add songs + my_playlist = MusicPlaylist("My Greatest Hits") + + for song in songs_to_add: + my_playlist.add_song(song) + + my_playlist.show_playlist_info() + + # Create music player + player = MusicPlayer("Spotify Player") + + # Demonstrate different iteration patterns + print("\n1. Forward Iteration:") + forward_iterator = my_playlist.create_iterator() + player.play(forward_iterator, "Sequential") + + print("2. Reverse Iteration:") + reverse_iterator = my_playlist.create_reverse_iterator() + player.play(reverse_iterator, "Reverse") + + print("3. Shuffle Iteration:") + shuffle_iterator = my_playlist.create_shuffle_iterator() + player.play(shuffle_iterator, "Shuffle") + + print("4. Genre Filter Iteration (Rock songs only):") + rock_iterator = my_playlist.create_genre_iterator("Rock") + player.play(rock_iterator, "Rock Filter") + + print("5. Genre Filter Iteration (Pop songs only):") + pop_iterator = my_playlist.create_genre_iterator("Pop") + player.play(pop_iterator, "Pop Filter") + + print("6. Limited Playback (First 3 songs):") + limited_iterator = my_playlist.create_iterator() + player.play_first(limited_iterator, 3, "Sequential") + + # Demonstrate iterator reset functionality + print("7. Iterator Reset Demonstration:") + reset_iterator = my_playlist.create_shuffle_iterator() + print("First shuffle:") + player.play_first(reset_iterator, 3, "Shuffle") + + reset_iterator.reset() + print("After reset - Second shuffle:") + player.play_first(reset_iterator, 3, "Shuffle") + + # Demonstrate multiple simultaneous iterators + print("8. Multiple Simultaneous Iterators:") + iter1 = my_playlist.create_iterator() + iter2 = my_playlist.create_reverse_iterator() + + print(f"Forward iterator - First song: {iter1.next().title if iter1.has_next() else 'None'}") + print(f"Reverse iterator - First song: {iter2.next().title if iter2.has_next() else 'None'}") + print(f"Forward iterator - Second song: {iter1.next().title if iter1.has_next() else 'None'}") + print(f"Reverse iterator - Second song: {iter2.next().title if iter2.has_next() else 'None'}") + + # Demonstrate Python's iterator protocol compatibility + print("\n9. Python Iterator Protocol Integration:") + + class PythonIteratorAdapter: + """Adapter to make our iterator work with Python's for loops""" + + def __init__(self, custom_iterator: Iterator[Song]): + self._iterator = custom_iterator + + def __iter__(self): + return self + + def __next__(self): + if self._iterator.has_next(): + return self._iterator.next() + else: + raise StopIteration + + print("Using Python's for loop with custom iterator:") + adapted_iterator = PythonIteratorAdapter(my_playlist.create_genre_iterator("Pop")) + for i, song in enumerate(adapted_iterator, 1): + print(f"{i}. {song.title} by {song.artist}") + + print("\n=== Iterator Pattern Benefits ===") + print("1. Uniform Interface: Same interface for different traversal algorithms") + print("2. Encapsulation: Internal structure of collection is hidden") + print("3. Multiple Iterators: Can have multiple iterators on same collection") + print("4. Polymorphic Iteration: Client code works with any iterator implementation") + print("5. Lazy Evaluation: Elements are accessed only when needed") + print("6. Memory Efficient: Don't need to load all elements at once") + print("7. Separation of Concerns: Iteration logic is separate from collection logic") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/mediator/solution.cpp b/src/content/design-pattern/code/mediator/solution.cpp new file mode 100644 index 0000000..4f4a9ed --- /dev/null +++ b/src/content/design-pattern/code/mediator/solution.cpp @@ -0,0 +1,584 @@ +// Mediator Pattern - Air Traffic Control System +// Centralizes complex communications and control logic between related objects + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +class Aircraft; + +// Enums +enum class AircraftStatus { + PARKED, + TAXIING, + TAKEOFF_REQUESTED, + TAKING_OFF, + IN_FLIGHT, + LANDING_REQUESTED, + LANDING, + EMERGENCY +}; + +// Utility function to convert enum to string +std::string statusToString(AircraftStatus status) { + switch (status) { + case AircraftStatus::PARKED: return "PARKED"; + case AircraftStatus::TAXIING: return "TAXIING"; + case AircraftStatus::TAKEOFF_REQUESTED: return "TAKEOFF_REQUESTED"; + case AircraftStatus::TAKING_OFF: return "TAKING_OFF"; + case AircraftStatus::IN_FLIGHT: return "IN_FLIGHT"; + case AircraftStatus::LANDING_REQUESTED: return "LANDING_REQUESTED"; + case AircraftStatus::LANDING: return "LANDING"; + case AircraftStatus::EMERGENCY: return "EMERGENCY"; + default: return "UNKNOWN"; + } +} + +// Abstract Mediator interface +class AirTrafficControlMediator { +public: + virtual ~AirTrafficControlMediator() = default; + virtual void requestTakeoff(std::shared_ptr aircraft) = 0; + virtual void requestLanding(std::shared_ptr aircraft) = 0; + virtual void requestEmergencyLanding(std::shared_ptr aircraft) = 0; + virtual void notifyAircraftPositionUpdate(std::shared_ptr aircraft, const std::string& position) = 0; + virtual void registerAircraft(std::shared_ptr aircraft) = 0; + virtual void unregisterAircraft(std::shared_ptr aircraft) = 0; +}; + +// Abstract Colleague class +class Aircraft { +protected: + std::shared_ptr mediator; + std::string callSign; + std::string aircraftType; + std::string currentPosition; + AircraftStatus status; + +public: + Aircraft(const std::string& cs, const std::string& type, + std::shared_ptr med) + : callSign(cs), aircraftType(type), mediator(med), + currentPosition("Gate"), status(AircraftStatus::PARKED) {} + + virtual ~Aircraft() = default; + + // Getters + std::string getCallSign() const { return callSign; } + std::string getAircraftType() const { return aircraftType; } + std::string getCurrentPosition() const { return currentPosition; } + AircraftStatus getStatus() const { return status; } + void setStatus(AircraftStatus newStatus) { status = newStatus; } + + // Abstract methods + virtual void receiveMessage(const std::string& message) = 0; + virtual void grantTakeoff() = 0; + virtual void grantLanding() = 0; + virtual void denyRequest(const std::string& reason) = 0; + + // Common methods + void requestTakeoff() { + if (status == AircraftStatus::PARKED || status == AircraftStatus::TAXIING) { + status = AircraftStatus::TAKEOFF_REQUESTED; + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": Requesting takeoff clearance" << std::endl; + mediator->requestTakeoff(shared_from_this()); + } else { + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": Cannot request takeoff in current status: " + << statusToString(status) << std::endl; + } + } + + void requestLanding() { + if (status == AircraftStatus::IN_FLIGHT) { + status = AircraftStatus::LANDING_REQUESTED; + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": Requesting landing clearance" << std::endl; + mediator->requestLanding(shared_from_this()); + } else { + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": Cannot request landing in current status: " + << statusToString(status) << std::endl; + } + } + + void declareEmergency() { + AircraftStatus previousStatus = status; + status = AircraftStatus::EMERGENCY; + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": EMERGENCY DECLARED! Previous status: " + << statusToString(previousStatus) << std::endl; + mediator->requestEmergencyLanding(shared_from_this()); + } + + void updatePosition(const std::string& newPosition) { + currentPosition = newPosition; + std::cout << "[" << getCurrentTime() << "] " << callSign + << ": Position update - " << newPosition << std::endl; + mediator->notifyAircraftPositionUpdate(shared_from_this(), newPosition); + } + + std::string toString() const { + return callSign + " (" + aircraftType + ") - " + + statusToString(status) + " at " + currentPosition; + } + + // Enable shared_from_this + virtual std::shared_ptr shared_from_this() = 0; + +protected: + std::string getCurrentTime() const { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + auto tm = *std::localtime(&time_t); + + std::stringstream ss; + ss << std::put_time(&tm, "%H:%M:%S"); + return ss.str(); + } +}; + +// Concrete Colleague classes +class CommercialAircraft : public Aircraft, public std::enable_shared_from_this { +private: + int passengerCount; + +public: + CommercialAircraft(const std::string& cs, const std::string& type, int passengers, + std::shared_ptr med) + : Aircraft(cs, type, med), passengerCount(passengers) { + med->registerAircraft(shared_from_this()); + } + + std::shared_ptr shared_from_this() override { + return std::enable_shared_from_this::shared_from_this(); + } + + void receiveMessage(const std::string& message) override { + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Commercial): Received - " << message << std::endl; + } + + void grantTakeoff() override { + status = AircraftStatus::TAKING_OFF; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Commercial): Takeoff granted. Taking off with " + << passengerCount << " passengers." << std::endl; + + // Simulate takeoff sequence + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(2)); + status = AircraftStatus::IN_FLIGHT; + updatePosition("Airspace Sector 1"); + }).detach(); + } + + void grantLanding() override { + status = AircraftStatus::LANDING; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Commercial): Landing granted. Approaching runway." << std::endl; + + // Simulate landing sequence + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + status = AircraftStatus::PARKED; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, 20); + int gateNumber = dis(gen); + + updatePosition("Gate " + std::to_string(gateNumber)); + }).detach(); + } + + void denyRequest(const std::string& reason) override { + if (status == AircraftStatus::TAKEOFF_REQUESTED) { + status = AircraftStatus::TAXIING; + } else if (status == AircraftStatus::LANDING_REQUESTED) { + status = AircraftStatus::IN_FLIGHT; + } + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Commercial): Request denied - " << reason << std::endl; + } +}; + +class CargoAircraft : public Aircraft, public std::enable_shared_from_this { +private: + double cargoWeight; + +public: + CargoAircraft(const std::string& cs, const std::string& type, double weight, + std::shared_ptr med) + : Aircraft(cs, type, med), cargoWeight(weight) { + med->registerAircraft(shared_from_this()); + } + + std::shared_ptr shared_from_this() override { + return std::enable_shared_from_this::shared_from_this(); + } + + void receiveMessage(const std::string& message) override { + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Cargo): Received - " << message << std::endl; + } + + void grantTakeoff() override { + status = AircraftStatus::TAKING_OFF; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Cargo): Takeoff granted. Departing with " + << cargoWeight << " tons of cargo." << std::endl; + + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::milliseconds(2500)); + status = AircraftStatus::IN_FLIGHT; + updatePosition("Cargo Route Alpha"); + }).detach(); + } + + void grantLanding() override { + status = AircraftStatus::LANDING; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Cargo): Landing granted. Approaching cargo terminal." << std::endl; + + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(2)); + status = AircraftStatus::PARKED; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 4); + char terminal = 'A' + dis(gen); + + updatePosition("Cargo Terminal " + std::string(1, terminal)); + }).detach(); + } + + void denyRequest(const std::string& reason) override { + if (status == AircraftStatus::TAKEOFF_REQUESTED) { + status = AircraftStatus::TAXIING; + } else if (status == AircraftStatus::LANDING_REQUESTED) { + status = AircraftStatus::IN_FLIGHT; + } + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Cargo): Request denied - " << reason << std::endl; + } +}; + +class PrivateJet : public Aircraft, public std::enable_shared_from_this { +private: + std::string owner; + +public: + PrivateJet(const std::string& cs, const std::string& type, const std::string& o, + std::shared_ptr med) + : Aircraft(cs, type, med), owner(o) { + med->registerAircraft(shared_from_this()); + } + + std::shared_ptr shared_from_this() override { + return std::enable_shared_from_this::shared_from_this(); + } + + void receiveMessage(const std::string& message) override { + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Private): Received - " << message << std::endl; + } + + void grantTakeoff() override { + status = AircraftStatus::TAKING_OFF; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Private): Takeoff granted. " << owner << "'s jet departing." << std::endl; + + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + status = AircraftStatus::IN_FLIGHT; + updatePosition("VIP Airspace"); + }).detach(); + } + + void grantLanding() override { + status = AircraftStatus::LANDING; + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Private): Landing granted. Proceeding to VIP terminal." << std::endl; + + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + status = AircraftStatus::PARKED; + updatePosition("VIP Terminal"); + }).detach(); + } + + void denyRequest(const std::string& reason) override { + if (status == AircraftStatus::TAKEOFF_REQUESTED) { + status = AircraftStatus::TAXIING; + } else if (status == AircraftStatus::LANDING_REQUESTED) { + status = AircraftStatus::IN_FLIGHT; + } + std::cout << "[" << getCurrentTime() << "] " << callSign + << " (Private): Request denied - " << reason << std::endl; + } +}; + +// Concrete Mediator +class AirTrafficControlTower : public AirTrafficControlMediator { +private: + std::vector> registeredAircraft; + std::queue> takeoffQueue; + std::queue> landingQueue; + bool runwayOccupied; + std::shared_ptr currentRunwayUser; + mutable std::mutex towerMutex; + +public: + AirTrafficControlTower() : runwayOccupied(false) {} + + void registerAircraft(std::shared_ptr aircraft) override { + std::lock_guard lock(towerMutex); + registeredAircraft.push_back(aircraft); + std::cout << "[" << getCurrentTime() << "] ATC: Aircraft registered - " + << aircraft->getCallSign() << std::endl; + broadcastToAllAircraft("New aircraft in controlled airspace: " + aircraft->getCallSign()); + } + + void unregisterAircraft(std::shared_ptr aircraft) override { + std::lock_guard lock(towerMutex); + auto it = std::find(registeredAircraft.begin(), registeredAircraft.end(), aircraft); + if (it != registeredAircraft.end()) { + registeredAircraft.erase(it); + } + std::cout << "[" << getCurrentTime() << "] ATC: Aircraft unregistered - " + << aircraft->getCallSign() << std::endl; + } + + void requestTakeoff(std::shared_ptr aircraft) override { + std::lock_guard lock(towerMutex); + std::cout << "[" << getCurrentTime() << "] ATC: Takeoff request received from " + << aircraft->getCallSign() << std::endl; + + if (canGrantTakeoff()) { + grantTakeoffImmediately(aircraft); + } else { + takeoffQueue.push(aircraft); + aircraft->receiveMessage("Added to takeoff queue. Position: " + + std::to_string(takeoffQueue.size())); + std::cout << "[" << getCurrentTime() << "] ATC: " << aircraft->getCallSign() + << " queued for takeoff" << std::endl; + } + } + + void requestLanding(std::shared_ptr aircraft) override { + std::lock_guard lock(towerMutex); + std::cout << "[" << getCurrentTime() << "] ATC: Landing request received from " + << aircraft->getCallSign() << std::endl; + + if (canGrantLanding()) { + grantLandingImmediately(aircraft); + } else { + landingQueue.push(aircraft); + aircraft->receiveMessage("Added to landing queue. Position: " + + std::to_string(landingQueue.size())); + std::cout << "[" << getCurrentTime() << "] ATC: " << aircraft->getCallSign() + << " queued for landing" << std::endl; + } + } + + void requestEmergencyLanding(std::shared_ptr aircraft) override { + std::lock_guard lock(towerMutex); + std::cout << "[" << getCurrentTime() << "] ATC: EMERGENCY LANDING request from " + << aircraft->getCallSign() << std::endl; + + if (runwayOccupied && currentRunwayUser) { + currentRunwayUser->receiveMessage("Emergency landing in progress. Expedite your operation."); + } + + broadcastToAllAircraft("Emergency landing in progress: " + aircraft->getCallSign() + + ". All aircraft standby."); + aircraft->receiveMessage("Emergency landing approved. Priority clearance granted."); + aircraft->grantLanding(); + + runwayOccupied = true; + currentRunwayUser = aircraft; + } + + void notifyAircraftPositionUpdate(std::shared_ptr aircraft, + const std::string& position) override { + std::cout << "[" << getCurrentTime() << "] ATC: Position update logged for " + << aircraft->getCallSign() << std::endl; + + // If aircraft has completed runway operation, process next in queue + if (currentRunwayUser == aircraft) { + if (position.find("Gate") == 0 || position.find("VIP Terminal") == 0 || + position.find("Cargo Terminal") == 0 || position.find("Airspace") != std::string::npos || + position.find("Route") != std::string::npos) { + + std::lock_guard lock(towerMutex); + runwayOccupied = false; + currentRunwayUser = nullptr; + processNextOperation(); + } + } + } + + void showSystemStatus() const { + std::lock_guard lock(towerMutex); + std::cout << "\n[" << getCurrentTime() << "] ===== ATC SYSTEM STATUS =====" << std::endl; + + std::string runwayStatus = runwayOccupied ? + ("OCCUPIED by " + currentRunwayUser->getCallSign()) : "CLEAR"; + std::cout << "Runway Status: " << runwayStatus << std::endl; + std::cout << "Takeoff Queue: " << takeoffQueue.size() << " aircraft waiting" << std::endl; + std::cout << "Landing Queue: " << landingQueue.size() << " aircraft waiting" << std::endl; + std::cout << "Total Registered Aircraft: " << registeredAircraft.size() << std::endl; + + std::cout << "\nAircraft Status:" << std::endl; + for (const auto& aircraft : registeredAircraft) { + std::cout << " - " << aircraft->toString() << std::endl; + } + std::cout << "=====================================\n" << std::endl; + } + +private: + bool canGrantTakeoff() const { + return !runwayOccupied && landingQueue.empty(); + } + + bool canGrantLanding() const { + return !runwayOccupied; + } + + void grantTakeoffImmediately(std::shared_ptr aircraft) { + runwayOccupied = true; + currentRunwayUser = aircraft; + aircraft->grantTakeoff(); + broadcastToAllAircraftExcept(aircraft, "Aircraft " + aircraft->getCallSign() + + " cleared for takeoff"); + } + + void grantLandingImmediately(std::shared_ptr aircraft) { + runwayOccupied = true; + currentRunwayUser = aircraft; + aircraft->grantLanding(); + broadcastToAllAircraftExcept(aircraft, "Aircraft " + aircraft->getCallSign() + + " cleared for landing"); + } + + void processNextOperation() { + if (!landingQueue.empty()) { + auto nextToLand = landingQueue.front(); + landingQueue.pop(); + std::cout << "[" << getCurrentTime() << "] ATC: Processing next landing request" << std::endl; + grantLandingImmediately(nextToLand); + } else if (!takeoffQueue.empty()) { + auto nextToTakeoff = takeoffQueue.front(); + takeoffQueue.pop(); + std::cout << "[" << getCurrentTime() << "] ATC: Processing next takeoff request" << std::endl; + grantTakeoffImmediately(nextToTakeoff); + } + } + + void broadcastToAllAircraft(const std::string& message) const { + std::cout << "[" << getCurrentTime() << "] ATC: Broadcasting - " << message << std::endl; + for (const auto& aircraft : registeredAircraft) { + aircraft->receiveMessage(message); + } + } + + void broadcastToAllAircraftExcept(std::shared_ptr excluded, + const std::string& message) const { + for (const auto& aircraft : registeredAircraft) { + if (aircraft != excluded) { + aircraft->receiveMessage(message); + } + } + } + + std::string getCurrentTime() const { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + auto tm = *std::localtime(&time_t); + + std::stringstream ss; + ss << std::put_time(&tm, "%H:%M:%S"); + return ss.str(); + } +}; + +// Main demonstration +int main() { + std::cout << "=== Mediator Pattern - Air Traffic Control System ===\n" << std::endl; + + // Create the mediator (Air Traffic Control Tower) + auto atcTower = std::make_shared(); + + // Create different types of aircraft + auto flight1 = std::make_shared("AA101", "Boeing 737", 180, atcTower); + auto flight2 = std::make_shared("UA205", "Airbus A320", 150, atcTower); + auto cargo1 = std::make_shared("FX789", "Boeing 747F", 120.5, atcTower); + auto jet1 = std::make_shared("N123PJ", "Gulfstream G650", "Tech CEO", atcTower); + auto flight3 = std::make_shared("DL456", "Boeing 777", 300, atcTower); + + std::cout << "\n1. Initial System Status:" << std::endl; + atcTower->showSystemStatus(); + + std::cout << "2. Multiple Takeoff Requests:" << std::endl; + flight1->requestTakeoff(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + cargo1->requestTakeoff(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + jet1->requestTakeoff(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + atcTower->showSystemStatus(); + + std::cout << "3. Simulating Aircraft in Flight Requesting Landing:" << std::endl; + flight2->updatePosition("Approaching Airspace"); + flight2->setStatus(AircraftStatus::IN_FLIGHT); + flight2->requestLanding(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + flight3->updatePosition("10 miles out"); + flight3->setStatus(AircraftStatus::IN_FLIGHT); + flight3->requestLanding(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + + atcTower->showSystemStatus(); + + std::cout << "4. Emergency Scenario:" << std::endl; + auto emergencyFlight = std::make_shared("EM999", "Airbus A330", 250, atcTower); + emergencyFlight->updatePosition("Emergency Approach"); + emergencyFlight->setStatus(AircraftStatus::IN_FLIGHT); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + emergencyFlight->declareEmergency(); + std::this_thread::sleep_for(std::chrono::seconds(3)); + + atcTower->showSystemStatus(); + + std::cout << "5. System Continues Processing Queue:" << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + atcTower->showSystemStatus(); + + std::cout << "=== Mediator Pattern Benefits ===" << std::endl; + std::cout << "1. Loose Coupling: Aircraft don't need to communicate directly with each other" << std::endl; + std::cout << "2. Centralized Control: All coordination logic is centralized in the mediator" << std::endl; + std::cout << "3. Reusable Components: Aircraft classes can be reused with different mediators" << std::endl; + std::cout << "4. Easy to Extend: New aircraft types can be added without changing existing ones" << std::endl; + std::cout << "5. Complex Interactions: Mediator handles complex coordination scenarios" << std::endl; + std::cout << "6. Single Responsibility: Each aircraft focuses on its own behavior" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/mediator/solution.java b/src/content/design-pattern/code/mediator/solution.java new file mode 100644 index 0000000..e518d21 --- /dev/null +++ b/src/content/design-pattern/code/mediator/solution.java @@ -0,0 +1,514 @@ +// Mediator Pattern - Air Traffic Control System +// Centralizes complex communications and control logic between related objects + +import java.util.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +// Mediator interface +interface AirTrafficControlMediator { + void requestTakeoff(Aircraft aircraft); + void requestLanding(Aircraft aircraft); + void requestEmergencyLanding(Aircraft aircraft); + void notifyAircraftPositionUpdate(Aircraft aircraft, String position); + void registerAircraft(Aircraft aircraft); + void unregisterAircraft(Aircraft aircraft); +} + +// Abstract Colleague class +abstract class Aircraft { + protected AirTrafficControlMediator mediator; + protected String callSign; + protected String aircraftType; + protected String currentPosition; + protected AircraftStatus status; + + public enum AircraftStatus { + PARKED, TAXIING, TAKEOFF_REQUESTED, TAKING_OFF, IN_FLIGHT, + LANDING_REQUESTED, LANDING, EMERGENCY + } + + public Aircraft(String callSign, String aircraftType, AirTrafficControlMediator mediator) { + this.callSign = callSign; + this.aircraftType = aircraftType; + this.mediator = mediator; + this.currentPosition = "Gate"; + this.status = AircraftStatus.PARKED; + this.mediator.registerAircraft(this); + } + + // Getters + public String getCallSign() { return callSign; } + public String getAircraftType() { return aircraftType; } + public String getCurrentPosition() { return currentPosition; } + public AircraftStatus getStatus() { return status; } + + // Abstract methods to be implemented by concrete aircraft + public abstract void receiveMessage(String message); + public abstract void grantTakeoff(); + public abstract void grantLanding(); + public abstract void denyRequest(String reason); + + // Common methods + public void requestTakeoff() { + if (status == AircraftStatus.PARKED || status == AircraftStatus.TAXIING) { + status = AircraftStatus.TAKEOFF_REQUESTED; + System.out.println("[" + getCurrentTime() + "] " + callSign + ": Requesting takeoff clearance"); + mediator.requestTakeoff(this); + } else { + System.out.println("[" + getCurrentTime() + "] " + callSign + ": Cannot request takeoff in current status: " + status); + } + } + + public void requestLanding() { + if (status == AircraftStatus.IN_FLIGHT) { + status = AircraftStatus.LANDING_REQUESTED; + System.out.println("[" + getCurrentTime() + "] " + callSign + ": Requesting landing clearance"); + mediator.requestLanding(this); + } else { + System.out.println("[" + getCurrentTime() + "] " + callSign + ": Cannot request landing in current status: " + status); + } + } + + public void declareEmergency() { + AircraftStatus previousStatus = status; + status = AircraftStatus.EMERGENCY; + System.out.println("[" + getCurrentTime() + "] " + callSign + ": EMERGENCY DECLARED! Previous status: " + previousStatus); + mediator.requestEmergencyLanding(this); + } + + public void updatePosition(String newPosition) { + this.currentPosition = newPosition; + System.out.println("[" + getCurrentTime() + "] " + callSign + ": Position update - " + newPosition); + mediator.notifyAircraftPositionUpdate(this, newPosition); + } + + protected String getCurrentTime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); + } + + @Override + public String toString() { + return callSign + " (" + aircraftType + ") - " + status + " at " + currentPosition; + } +} + +// Concrete Colleague classes +class CommercialAircraft extends Aircraft { + private int passengerCount; + + public CommercialAircraft(String callSign, String aircraftType, int passengerCount, + AirTrafficControlMediator mediator) { + super(callSign, aircraftType, mediator); + this.passengerCount = passengerCount; + } + + @Override + public void receiveMessage(String message) { + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Commercial): Received - " + message); + } + + @Override + public void grantTakeoff() { + status = AircraftStatus.TAKING_OFF; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Commercial): Takeoff granted. Taking off with " + passengerCount + " passengers."); + + // Simulate takeoff sequence + new Thread(() -> { + try { + Thread.sleep(2000); + status = AircraftStatus.IN_FLIGHT; + updatePosition("Airspace Sector 1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void grantLanding() { + status = AircraftStatus.LANDING; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Commercial): Landing granted. Approaching runway."); + + // Simulate landing sequence + new Thread(() -> { + try { + Thread.sleep(1500); + status = AircraftStatus.PARKED; + updatePosition("Gate " + (new Random().nextInt(20) + 1)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void denyRequest(String reason) { + if (status == AircraftStatus.TAKEOFF_REQUESTED) { + status = AircraftStatus.TAXIING; + } else if (status == AircraftStatus.LANDING_REQUESTED) { + status = AircraftStatus.IN_FLIGHT; + } + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Commercial): Request denied - " + reason); + } +} + +class CargoAircraft extends Aircraft { + private double cargoWeight; + + public CargoAircraft(String callSign, String aircraftType, double cargoWeight, + AirTrafficControlMediator mediator) { + super(callSign, aircraftType, mediator); + this.cargoWeight = cargoWeight; + } + + @Override + public void receiveMessage(String message) { + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Cargo): Received - " + message); + } + + @Override + public void grantTakeoff() { + status = AircraftStatus.TAKING_OFF; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Cargo): Takeoff granted. Departing with " + cargoWeight + " tons of cargo."); + + new Thread(() -> { + try { + Thread.sleep(2500); // Cargo planes take longer to takeoff + status = AircraftStatus.IN_FLIGHT; + updatePosition("Cargo Route Alpha"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void grantLanding() { + status = AircraftStatus.LANDING; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Cargo): Landing granted. Approaching cargo terminal."); + + new Thread(() -> { + try { + Thread.sleep(2000); // Cargo planes take longer to land + status = AircraftStatus.PARKED; + updatePosition("Cargo Terminal " + (char)('A' + new Random().nextInt(5))); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void denyRequest(String reason) { + if (status == AircraftStatus.TAKEOFF_REQUESTED) { + status = AircraftStatus.TAXIING; + } else if (status == AircraftStatus.LANDING_REQUESTED) { + status = AircraftStatus.IN_FLIGHT; + } + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Cargo): Request denied - " + reason); + } +} + +class PrivateJet extends Aircraft { + private String owner; + + public PrivateJet(String callSign, String aircraftType, String owner, + AirTrafficControlMediator mediator) { + super(callSign, aircraftType, mediator); + this.owner = owner; + } + + @Override + public void receiveMessage(String message) { + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Private): Received - " + message); + } + + @Override + public void grantTakeoff() { + status = AircraftStatus.TAKING_OFF; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Private): Takeoff granted. " + owner + "'s jet departing."); + + new Thread(() -> { + try { + Thread.sleep(1000); // Private jets are faster + status = AircraftStatus.IN_FLIGHT; + updatePosition("VIP Airspace"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void grantLanding() { + status = AircraftStatus.LANDING; + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Private): Landing granted. Proceeding to VIP terminal."); + + new Thread(() -> { + try { + Thread.sleep(1000); + status = AircraftStatus.PARKED; + updatePosition("VIP Terminal"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public void denyRequest(String reason) { + if (status == AircraftStatus.TAKEOFF_REQUESTED) { + status = AircraftStatus.TAXIING; + } else if (status == AircraftStatus.LANDING_REQUESTED) { + status = AircraftStatus.IN_FLIGHT; + } + System.out.println("[" + getCurrentTime() + "] " + callSign + " (Private): Request denied - " + reason); + } +} + +// Concrete Mediator +class AirTrafficControlTower implements AirTrafficControlMediator { + private List registeredAircraft; + private Queue takeoffQueue; + private Queue landingQueue; + private boolean runwayOccupied; + private Aircraft currentRunwayUser; + private int maxConcurrentOperations; + + public AirTrafficControlTower() { + this.registeredAircraft = new ArrayList<>(); + this.takeoffQueue = new LinkedList<>(); + this.landingQueue = new LinkedList<>(); + this.runwayOccupied = false; + this.maxConcurrentOperations = 2; // Allow 2 concurrent operations + } + + @Override + public void registerAircraft(Aircraft aircraft) { + registeredAircraft.add(aircraft); + System.out.println("[" + getCurrentTime() + "] ATC: Aircraft registered - " + aircraft.getCallSign()); + broadcastToAllAircraft("New aircraft in controlled airspace: " + aircraft.getCallSign()); + } + + @Override + public void unregisterAircraft(Aircraft aircraft) { + registeredAircraft.remove(aircraft); + takeoffQueue.remove(aircraft); + landingQueue.remove(aircraft); + System.out.println("[" + getCurrentTime() + "] ATC: Aircraft unregistered - " + aircraft.getCallSign()); + } + + @Override + public void requestTakeoff(Aircraft aircraft) { + System.out.println("[" + getCurrentTime() + "] ATC: Takeoff request received from " + aircraft.getCallSign()); + + if (canGrantTakeoff()) { + grantTakeoffImmediately(aircraft); + } else { + takeoffQueue.offer(aircraft); + aircraft.receiveMessage("Added to takeoff queue. Position: " + takeoffQueue.size()); + System.out.println("[" + getCurrentTime() + "] ATC: " + aircraft.getCallSign() + " queued for takeoff"); + } + } + + @Override + public void requestLanding(Aircraft aircraft) { + System.out.println("[" + getCurrentTime() + "] ATC: Landing request received from " + aircraft.getCallSign()); + + if (canGrantLanding()) { + grantLandingImmediately(aircraft); + } else { + landingQueue.offer(aircraft); + aircraft.receiveMessage("Added to landing queue. Position: " + landingQueue.size()); + System.out.println("[" + getCurrentTime() + "] ATC: " + aircraft.getCallSign() + " queued for landing"); + } + } + + @Override + public void requestEmergencyLanding(Aircraft aircraft) { + System.out.println("[" + getCurrentTime() + "] ATC: EMERGENCY LANDING request from " + aircraft.getCallSign()); + + // Emergency aircraft get highest priority + if (runwayOccupied && currentRunwayUser != null) { + currentRunwayUser.receiveMessage("Emergency landing in progress. Expedite your operation."); + } + + // Clear the aircraft from regular queues and grant immediate landing + takeoffQueue.remove(aircraft); + landingQueue.remove(aircraft); + + broadcastToAllAircraft("Emergency landing in progress: " + aircraft.getCallSign() + ". All aircraft standby."); + aircraft.receiveMessage("Emergency landing approved. Priority clearance granted."); + aircraft.grantLanding(); + + runwayOccupied = true; + currentRunwayUser = aircraft; + } + + @Override + public void notifyAircraftPositionUpdate(Aircraft aircraft, String position) { + System.out.println("[" + getCurrentTime() + "] ATC: Position update logged for " + aircraft.getCallSign()); + + // If aircraft has completed runway operation, process next in queue + if ((position.startsWith("Gate") || position.startsWith("VIP Terminal") || + position.startsWith("Cargo Terminal")) && currentRunwayUser == aircraft) { + runwayOccupied = false; + currentRunwayUser = null; + processNextOperation(); + } else if (position.contains("Airspace") || position.contains("Route") && + currentRunwayUser == aircraft) { + runwayOccupied = false; + currentRunwayUser = null; + processNextOperation(); + } + } + + private boolean canGrantTakeoff() { + return !runwayOccupied && landingQueue.isEmpty(); // Landing has priority over takeoff + } + + private boolean canGrantLanding() { + return !runwayOccupied; + } + + private void grantTakeoffImmediately(Aircraft aircraft) { + runwayOccupied = true; + currentRunwayUser = aircraft; + aircraft.grantTakeoff(); + broadcastToAllAircraftExcept(aircraft, "Aircraft " + aircraft.getCallSign() + " cleared for takeoff"); + } + + private void grantLandingImmediately(Aircraft aircraft) { + runwayOccupied = true; + currentRunwayUser = aircraft; + aircraft.grantLanding(); + broadcastToAllAircraftExcept(aircraft, "Aircraft " + aircraft.getCallSign() + " cleared for landing"); + } + + private void processNextOperation() { + // Landing requests have priority over takeoff requests + if (!landingQueue.isEmpty()) { + Aircraft nextToLand = landingQueue.poll(); + System.out.println("[" + getCurrentTime() + "] ATC: Processing next landing request"); + grantLandingImmediately(nextToLand); + } else if (!takeoffQueue.isEmpty()) { + Aircraft nextToTakeoff = takeoffQueue.poll(); + System.out.println("[" + getCurrentTime() + "] ATC: Processing next takeoff request"); + grantTakeoffImmediately(nextToTakeoff); + } + } + + private void broadcastToAllAircraft(String message) { + System.out.println("[" + getCurrentTime() + "] ATC: Broadcasting - " + message); + for (Aircraft aircraft : registeredAircraft) { + aircraft.receiveMessage(message); + } + } + + private void broadcastToAllAircraftExcept(Aircraft excluded, String message) { + for (Aircraft aircraft : registeredAircraft) { + if (aircraft != excluded) { + aircraft.receiveMessage(message); + } + } + } + + public void showSystemStatus() { + System.out.println("\n[" + getCurrentTime() + "] ===== ATC SYSTEM STATUS ====="); + System.out.println("Runway Status: " + (runwayOccupied ? "OCCUPIED by " + currentRunwayUser.getCallSign() : "CLEAR")); + System.out.println("Takeoff Queue: " + takeoffQueue.size() + " aircraft waiting"); + System.out.println("Landing Queue: " + landingQueue.size() + " aircraft waiting"); + System.out.println("Total Registered Aircraft: " + registeredAircraft.size()); + + System.out.println("\nAircraft Status:"); + for (Aircraft aircraft : registeredAircraft) { + System.out.println(" - " + aircraft.toString()); + } + System.out.println("=====================================\n"); + } + + private String getCurrentTime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); + } +} + +// Main demonstration class +public class MediatorPatternDemo { + public static void main(String[] args) throws InterruptedException { + System.out.println("=== Mediator Pattern - Air Traffic Control System ===\n"); + + // Create the mediator (Air Traffic Control Tower) + AirTrafficControlTower atcTower = new AirTrafficControlTower(); + + // Create different types of aircraft + CommercialAircraft flight1 = new CommercialAircraft("AA101", "Boeing 737", 180, atcTower); + CommercialAircraft flight2 = new CommercialAircraft("UA205", "Airbus A320", 150, atcTower); + CargoAircraft cargo1 = new CargoAircraft("FX789", "Boeing 747F", 120.5, atcTower); + PrivateJet jet1 = new PrivateJet("N123PJ", "Gulfstream G650", "Tech CEO", atcTower); + CommercialAircraft flight3 = new CommercialAircraft("DL456", "Boeing 777", 300, atcTower); + + System.out.println("\n1. Initial System Status:"); + atcTower.showSystemStatus(); + + System.out.println("2. Multiple Takeoff Requests:"); + // Simulate multiple takeoff requests + flight1.requestTakeoff(); + Thread.sleep(500); + + cargo1.requestTakeoff(); + Thread.sleep(500); + + jet1.requestTakeoff(); + Thread.sleep(1000); + + atcTower.showSystemStatus(); + + System.out.println("3. Simulating Aircraft in Flight Requesting Landing:"); + // Simulate some aircraft already in flight requesting landing + flight2.updatePosition("Approaching Airspace"); + flight2.status = Aircraft.AircraftStatus.IN_FLIGHT; + flight2.requestLanding(); + Thread.sleep(500); + + flight3.updatePosition("10 miles out"); + flight3.status = Aircraft.AircraftStatus.IN_FLIGHT; + flight3.requestLanding(); + Thread.sleep(2000); + + atcTower.showSystemStatus(); + + System.out.println("4. Emergency Scenario:"); + // Create emergency aircraft + CommercialAircraft emergencyFlight = new CommercialAircraft("EM999", "Airbus A330", 250, atcTower); + emergencyFlight.updatePosition("Emergency Approach"); + emergencyFlight.status = Aircraft.AircraftStatus.IN_FLIGHT; + Thread.sleep(1000); + + // Declare emergency + emergencyFlight.declareEmergency(); + Thread.sleep(3000); + + atcTower.showSystemStatus(); + + System.out.println("5. System Continues Processing Queue:"); + // Wait for operations to complete and show final status + Thread.sleep(5000); + atcTower.showSystemStatus(); + + System.out.println("=== Mediator Pattern Benefits ==="); + System.out.println("1. Loose Coupling: Aircraft don't need to communicate directly with each other"); + System.out.println("2. Centralized Control: All coordination logic is centralized in the mediator"); + System.out.println("3. Reusable Components: Aircraft classes can be reused with different mediators"); + System.out.println("4. Easy to Extend: New aircraft types can be added without changing existing ones"); + System.out.println("5. Complex Interactions: Mediator handles complex coordination scenarios"); + System.out.println("6. Single Responsibility: Each aircraft focuses on its own behavior"); + + System.out.println("\n=== Real-world Applications ==="); + System.out.println("- Chat rooms and messaging systems"); + System.out.println("- GUI frameworks (component communication)"); + System.out.println("- Workflow management systems"); + System.out.println("- Game development (entity interactions)"); + System.out.println("- Microservice orchestration"); + } +} diff --git a/src/content/design-pattern/code/mediator/solution.py b/src/content/design-pattern/code/mediator/solution.py new file mode 100644 index 0000000..0be0a75 --- /dev/null +++ b/src/content/design-pattern/code/mediator/solution.py @@ -0,0 +1,477 @@ +""" +Mediator Pattern - Air Traffic Control System +Centralizes complex communications and control logic between related objects +""" + +from abc import ABC, abstractmethod +from enum import Enum +from typing import List, Optional, Deque +from collections import deque +import threading +import time +from datetime import datetime +import random + +class AircraftStatus(Enum): + PARKED = "PARKED" + TAXIING = "TAXIING" + TAKEOFF_REQUESTED = "TAKEOFF_REQUESTED" + TAKING_OFF = "TAKING_OFF" + IN_FLIGHT = "IN_FLIGHT" + LANDING_REQUESTED = "LANDING_REQUESTED" + LANDING = "LANDING" + EMERGENCY = "EMERGENCY" + +class AirTrafficControlMediator(ABC): + """Abstract mediator interface for air traffic control""" + + @abstractmethod + def request_takeoff(self, aircraft: 'Aircraft') -> None: + pass + + @abstractmethod + def request_landing(self, aircraft: 'Aircraft') -> None: + pass + + @abstractmethod + def request_emergency_landing(self, aircraft: 'Aircraft') -> None: + pass + + @abstractmethod + def notify_aircraft_position_update(self, aircraft: 'Aircraft', position: str) -> None: + pass + + @abstractmethod + def register_aircraft(self, aircraft: 'Aircraft') -> None: + pass + + @abstractmethod + def unregister_aircraft(self, aircraft: 'Aircraft') -> None: + pass + +class Aircraft(ABC): + """Abstract colleague class representing an aircraft""" + + def __init__(self, call_sign: str, aircraft_type: str, mediator: AirTrafficControlMediator): + self._call_sign = call_sign + self._aircraft_type = aircraft_type + self._mediator = mediator + self._current_position = "Gate" + self._status = AircraftStatus.PARKED + self._mediator.register_aircraft(self) + + @property + def call_sign(self) -> str: + return self._call_sign + + @property + def aircraft_type(self) -> str: + return self._aircraft_type + + @property + def current_position(self) -> str: + return self._current_position + + @property + def status(self) -> AircraftStatus: + return self._status + + @status.setter + def status(self, value: AircraftStatus) -> None: + self._status = value + + # Abstract methods to be implemented by concrete aircraft + @abstractmethod + def receive_message(self, message: str) -> None: + pass + + @abstractmethod + def grant_takeoff(self) -> None: + pass + + @abstractmethod + def grant_landing(self) -> None: + pass + + @abstractmethod + def deny_request(self, reason: str) -> None: + pass + + # Common methods + def request_takeoff(self) -> None: + if self._status in [AircraftStatus.PARKED, AircraftStatus.TAXIING]: + self._status = AircraftStatus.TAKEOFF_REQUESTED + print(f"[{self._get_current_time()}] {self._call_sign}: Requesting takeoff clearance") + self._mediator.request_takeoff(self) + else: + print(f"[{self._get_current_time()}] {self._call_sign}: Cannot request takeoff in current status: {self._status.value}") + + def request_landing(self) -> None: + if self._status == AircraftStatus.IN_FLIGHT: + self._status = AircraftStatus.LANDING_REQUESTED + print(f"[{self._get_current_time()}] {self._call_sign}: Requesting landing clearance") + self._mediator.request_landing(self) + else: + print(f"[{self._get_current_time()}] {self._call_sign}: Cannot request landing in current status: {self._status.value}") + + def declare_emergency(self) -> None: + previous_status = self._status + self._status = AircraftStatus.EMERGENCY + print(f"[{self._get_current_time()}] {self._call_sign}: EMERGENCY DECLARED! Previous status: {previous_status.value}") + self._mediator.request_emergency_landing(self) + + def update_position(self, new_position: str) -> None: + self._current_position = new_position + print(f"[{self._get_current_time()}] {self._call_sign}: Position update - {new_position}") + self._mediator.notify_aircraft_position_update(self, new_position) + + def _get_current_time(self) -> str: + return datetime.now().strftime("%H:%M:%S") + + def __str__(self) -> str: + return f"{self._call_sign} ({self._aircraft_type}) - {self._status.value} at {self._current_position}" + +class CommercialAircraft(Aircraft): + """Commercial aircraft with passenger capacity""" + + def __init__(self, call_sign: str, aircraft_type: str, passenger_count: int, + mediator: AirTrafficControlMediator): + super().__init__(call_sign, aircraft_type, mediator) + self._passenger_count = passenger_count + + def receive_message(self, message: str) -> None: + print(f"[{self._get_current_time()}] {self._call_sign} (Commercial): Received - {message}") + + def grant_takeoff(self) -> None: + self._status = AircraftStatus.TAKING_OFF + print(f"[{self._get_current_time()}] {self._call_sign} (Commercial): Takeoff granted. Taking off with {self._passenger_count} passengers.") + + # Simulate takeoff sequence in separate thread + def takeoff_sequence(): + time.sleep(2) + self._status = AircraftStatus.IN_FLIGHT + self.update_position("Airspace Sector 1") + + threading.Thread(target=takeoff_sequence, daemon=True).start() + + def grant_landing(self) -> None: + self._status = AircraftStatus.LANDING + print(f"[{self._get_current_time()}] {self._call_sign} (Commercial): Landing granted. Approaching runway.") + + # Simulate landing sequence + def landing_sequence(): + time.sleep(1.5) + self._status = AircraftStatus.PARKED + gate_number = random.randint(1, 20) + self.update_position(f"Gate {gate_number}") + + threading.Thread(target=landing_sequence, daemon=True).start() + + def deny_request(self, reason: str) -> None: + if self._status == AircraftStatus.TAKEOFF_REQUESTED: + self._status = AircraftStatus.TAXIING + elif self._status == AircraftStatus.LANDING_REQUESTED: + self._status = AircraftStatus.IN_FLIGHT + print(f"[{self._get_current_time()}] {self._call_sign} (Commercial): Request denied - {reason}") + +class CargoAircraft(Aircraft): + """Cargo aircraft with weight capacity""" + + def __init__(self, call_sign: str, aircraft_type: str, cargo_weight: float, + mediator: AirTrafficControlMediator): + super().__init__(call_sign, aircraft_type, mediator) + self._cargo_weight = cargo_weight + + def receive_message(self, message: str) -> None: + print(f"[{self._get_current_time()}] {self._call_sign} (Cargo): Received - {message}") + + def grant_takeoff(self) -> None: + self._status = AircraftStatus.TAKING_OFF + print(f"[{self._get_current_time()}] {self._call_sign} (Cargo): Takeoff granted. Departing with {self._cargo_weight} tons of cargo.") + + def takeoff_sequence(): + time.sleep(2.5) # Cargo planes take longer to takeoff + self._status = AircraftStatus.IN_FLIGHT + self.update_position("Cargo Route Alpha") + + threading.Thread(target=takeoff_sequence, daemon=True).start() + + def grant_landing(self) -> None: + self._status = AircraftStatus.LANDING + print(f"[{self._get_current_time()}] {self._call_sign} (Cargo): Landing granted. Approaching cargo terminal.") + + def landing_sequence(): + time.sleep(2) # Cargo planes take longer to land + self._status = AircraftStatus.PARKED + terminal = chr(ord('A') + random.randint(0, 4)) + self.update_position(f"Cargo Terminal {terminal}") + + threading.Thread(target=landing_sequence, daemon=True).start() + + def deny_request(self, reason: str) -> None: + if self._status == AircraftStatus.TAKEOFF_REQUESTED: + self._status = AircraftStatus.TAXIING + elif self._status == AircraftStatus.LANDING_REQUESTED: + self._status = AircraftStatus.IN_FLIGHT + print(f"[{self._get_current_time()}] {self._call_sign} (Cargo): Request denied - {reason}") + +class PrivateJet(Aircraft): + """Private jet with owner information""" + + def __init__(self, call_sign: str, aircraft_type: str, owner: str, + mediator: AirTrafficControlMediator): + super().__init__(call_sign, aircraft_type, mediator) + self._owner = owner + + def receive_message(self, message: str) -> None: + print(f"[{self._get_current_time()}] {self._call_sign} (Private): Received - {message}") + + def grant_takeoff(self) -> None: + self._status = AircraftStatus.TAKING_OFF + print(f"[{self._get_current_time()}] {self._call_sign} (Private): Takeoff granted. {self._owner}'s jet departing.") + + def takeoff_sequence(): + time.sleep(1) # Private jets are faster + self._status = AircraftStatus.IN_FLIGHT + self.update_position("VIP Airspace") + + threading.Thread(target=takeoff_sequence, daemon=True).start() + + def grant_landing(self) -> None: + self._status = AircraftStatus.LANDING + print(f"[{self._get_current_time()}] {self._call_sign} (Private): Landing granted. Proceeding to VIP terminal.") + + def landing_sequence(): + time.sleep(1) + self._status = AircraftStatus.PARKED + self.update_position("VIP Terminal") + + threading.Thread(target=landing_sequence, daemon=True).start() + + def deny_request(self, reason: str) -> None: + if self._status == AircraftStatus.TAKEOFF_REQUESTED: + self._status = AircraftStatus.TAXIING + elif self._status == AircraftStatus.LANDING_REQUESTED: + self._status = AircraftStatus.IN_FLIGHT + print(f"[{self._get_current_time()}] {self._call_sign} (Private): Request denied - {reason}") + +class AirTrafficControlTower(AirTrafficControlMediator): + """Concrete mediator - Air Traffic Control Tower""" + + def __init__(self): + self._registered_aircraft: List[Aircraft] = [] + self._takeoff_queue: Deque[Aircraft] = deque() + self._landing_queue: Deque[Aircraft] = deque() + self._runway_occupied = False + self._current_runway_user: Optional[Aircraft] = None + self._lock = threading.RLock() # Reentrant lock for thread safety + + def register_aircraft(self, aircraft: Aircraft) -> None: + with self._lock: + self._registered_aircraft.append(aircraft) + print(f"[{self._get_current_time()}] ATC: Aircraft registered - {aircraft.call_sign}") + self._broadcast_to_all_aircraft(f"New aircraft in controlled airspace: {aircraft.call_sign}") + + def unregister_aircraft(self, aircraft: Aircraft) -> None: + with self._lock: + if aircraft in self._registered_aircraft: + self._registered_aircraft.remove(aircraft) + if aircraft in self._takeoff_queue: + self._takeoff_queue.remove(aircraft) + if aircraft in self._landing_queue: + self._landing_queue.remove(aircraft) + print(f"[{self._get_current_time()}] ATC: Aircraft unregistered - {aircraft.call_sign}") + + def request_takeoff(self, aircraft: Aircraft) -> None: + with self._lock: + print(f"[{self._get_current_time()}] ATC: Takeoff request received from {aircraft.call_sign}") + + if self._can_grant_takeoff(): + self._grant_takeoff_immediately(aircraft) + else: + self._takeoff_queue.append(aircraft) + aircraft.receive_message(f"Added to takeoff queue. Position: {len(self._takeoff_queue)}") + print(f"[{self._get_current_time()}] ATC: {aircraft.call_sign} queued for takeoff") + + def request_landing(self, aircraft: Aircraft) -> None: + with self._lock: + print(f"[{self._get_current_time()}] ATC: Landing request received from {aircraft.call_sign}") + + if self._can_grant_landing(): + self._grant_landing_immediately(aircraft) + else: + self._landing_queue.append(aircraft) + aircraft.receive_message(f"Added to landing queue. Position: {len(self._landing_queue)}") + print(f"[{self._get_current_time()}] ATC: {aircraft.call_sign} queued for landing") + + def request_emergency_landing(self, aircraft: Aircraft) -> None: + with self._lock: + print(f"[{self._get_current_time()}] ATC: EMERGENCY LANDING request from {aircraft.call_sign}") + + # Emergency aircraft get highest priority + if self._runway_occupied and self._current_runway_user: + self._current_runway_user.receive_message("Emergency landing in progress. Expedite your operation.") + + # Remove aircraft from regular queues + if aircraft in self._takeoff_queue: + self._takeoff_queue.remove(aircraft) + if aircraft in self._landing_queue: + self._landing_queue.remove(aircraft) + + self._broadcast_to_all_aircraft(f"Emergency landing in progress: {aircraft.call_sign}. All aircraft standby.") + aircraft.receive_message("Emergency landing approved. Priority clearance granted.") + aircraft.grant_landing() + + self._runway_occupied = True + self._current_runway_user = aircraft + + def notify_aircraft_position_update(self, aircraft: Aircraft, position: str) -> None: + print(f"[{self._get_current_time()}] ATC: Position update logged for {aircraft.call_sign}") + + # If aircraft has completed runway operation, process next in queue + if self._current_runway_user == aircraft: + if (position.startswith("Gate") or position.startswith("VIP Terminal") or + position.startswith("Cargo Terminal") or "Airspace" in position or + "Route" in position): + + with self._lock: + self._runway_occupied = False + self._current_runway_user = None + self._process_next_operation() + + def _can_grant_takeoff(self) -> bool: + return not self._runway_occupied and len(self._landing_queue) == 0 # Landing has priority + + def _can_grant_landing(self) -> bool: + return not self._runway_occupied + + def _grant_takeoff_immediately(self, aircraft: Aircraft) -> None: + self._runway_occupied = True + self._current_runway_user = aircraft + aircraft.grant_takeoff() + self._broadcast_to_all_aircraft_except(aircraft, f"Aircraft {aircraft.call_sign} cleared for takeoff") + + def _grant_landing_immediately(self, aircraft: Aircraft) -> None: + self._runway_occupied = True + self._current_runway_user = aircraft + aircraft.grant_landing() + self._broadcast_to_all_aircraft_except(aircraft, f"Aircraft {aircraft.call_sign} cleared for landing") + + def _process_next_operation(self) -> None: + # Landing requests have priority over takeoff requests + if self._landing_queue: + next_to_land = self._landing_queue.popleft() + print(f"[{self._get_current_time()}] ATC: Processing next landing request") + self._grant_landing_immediately(next_to_land) + elif self._takeoff_queue: + next_to_takeoff = self._takeoff_queue.popleft() + print(f"[{self._get_current_time()}] ATC: Processing next takeoff request") + self._grant_takeoff_immediately(next_to_takeoff) + + def _broadcast_to_all_aircraft(self, message: str) -> None: + print(f"[{self._get_current_time()}] ATC: Broadcasting - {message}") + for aircraft in self._registered_aircraft: + aircraft.receive_message(message) + + def _broadcast_to_all_aircraft_except(self, excluded: Aircraft, message: str) -> None: + for aircraft in self._registered_aircraft: + if aircraft != excluded: + aircraft.receive_message(message) + + def show_system_status(self) -> None: + with self._lock: + print(f"\n[{self._get_current_time()}] ===== ATC SYSTEM STATUS =====") + runway_status = f"OCCUPIED by {self._current_runway_user.call_sign}" if self._runway_occupied else "CLEAR" + print(f"Runway Status: {runway_status}") + print(f"Takeoff Queue: {len(self._takeoff_queue)} aircraft waiting") + print(f"Landing Queue: {len(self._landing_queue)} aircraft waiting") + print(f"Total Registered Aircraft: {len(self._registered_aircraft)}") + + print("\nAircraft Status:") + for aircraft in self._registered_aircraft: + print(f" - {aircraft}") + print("=====================================\n") + + def _get_current_time(self) -> str: + return datetime.now().strftime("%H:%M:%S") + +def main(): + """Demonstrate the Mediator pattern""" + print("=== Mediator Pattern - Air Traffic Control System ===\n") + + # Create the mediator (Air Traffic Control Tower) + atc_tower = AirTrafficControlTower() + + # Create different types of aircraft + flight1 = CommercialAircraft("AA101", "Boeing 737", 180, atc_tower) + flight2 = CommercialAircraft("UA205", "Airbus A320", 150, atc_tower) + cargo1 = CargoAircraft("FX789", "Boeing 747F", 120.5, atc_tower) + jet1 = PrivateJet("N123PJ", "Gulfstream G650", "Tech CEO", atc_tower) + flight3 = CommercialAircraft("DL456", "Boeing 777", 300, atc_tower) + + print("\n1. Initial System Status:") + atc_tower.show_system_status() + + print("2. Multiple Takeoff Requests:") + # Simulate multiple takeoff requests + flight1.request_takeoff() + time.sleep(0.5) + + cargo1.request_takeoff() + time.sleep(0.5) + + jet1.request_takeoff() + time.sleep(1) + + atc_tower.show_system_status() + + print("3. Simulating Aircraft in Flight Requesting Landing:") + # Simulate some aircraft already in flight requesting landing + flight2.update_position("Approaching Airspace") + flight2.status = AircraftStatus.IN_FLIGHT + flight2.request_landing() + time.sleep(0.5) + + flight3.update_position("10 miles out") + flight3.status = AircraftStatus.IN_FLIGHT + flight3.request_landing() + time.sleep(2) + + atc_tower.show_system_status() + + print("4. Emergency Scenario:") + # Create emergency aircraft + emergency_flight = CommercialAircraft("EM999", "Airbus A330", 250, atc_tower) + emergency_flight.update_position("Emergency Approach") + emergency_flight.status = AircraftStatus.IN_FLIGHT + time.sleep(1) + + # Declare emergency + emergency_flight.declare_emergency() + time.sleep(3) + + atc_tower.show_system_status() + + print("5. System Continues Processing Queue:") + # Wait for operations to complete and show final status + time.sleep(5) + atc_tower.show_system_status() + + print("=== Mediator Pattern Benefits ===") + print("1. Loose Coupling: Aircraft don't need to communicate directly with each other") + print("2. Centralized Control: All coordination logic is centralized in the mediator") + print("3. Reusable Components: Aircraft classes can be reused with different mediators") + print("4. Easy to Extend: New aircraft types can be added without changing existing ones") + print("5. Complex Interactions: Mediator handles complex coordination scenarios") + print("6. Single Responsibility: Each aircraft focuses on its own behavior") + + print("\n=== Real-world Applications ===") + print("- Chat rooms and messaging systems") + print("- GUI frameworks (component communication)") + print("- Workflow management systems") + print("- Game development (entity interactions)") + print("- Microservice orchestration") + print("- Event-driven architectures") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/memento/solution.cpp b/src/content/design-pattern/code/memento/solution.cpp new file mode 100644 index 0000000..22a0dc0 --- /dev/null +++ b/src/content/design-pattern/code/memento/solution.cpp @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include + +// Memento class - stores the internal state of TextEditor +class TextEditorMemento { +private: + std::string content; + int cursorPosition; + std::string selectionText; + +public: + TextEditorMemento(const std::string& content, int cursorPosition, const std::string& selectionText) + : content(content), cursorPosition(cursorPosition), selectionText(selectionText) {} + + const std::string& getContent() const { return content; } + int getCursorPosition() const { return cursorPosition; } + const std::string& getSelectionText() const { return selectionText; } +}; + +// Originator - creates and restores mementos +class TextEditor { +private: + std::string content; + int cursorPosition; + std::string selectionText; + +public: + TextEditor() : content(""), cursorPosition(0), selectionText("") {} + + void write(const std::string& text) { + content.insert(cursorPosition, text); + cursorPosition += text.length(); + selectionText = ""; + } + + void deleteChars(int characters) { + int startPos = std::max(0, cursorPosition - characters); + content.erase(startPos, cursorPosition - startPos); + cursorPosition = startPos; + selectionText = ""; + } + + void setCursorPosition(int position) { + cursorPosition = std::max(0, std::min(position, static_cast(content.length()))); + } + + void selectText(int startPos, int endPos) { + if (startPos >= 0 && endPos <= content.length() && startPos <= endPos) { + selectionText = content.substr(startPos, endPos - startPos); + } + } + + // Create memento + std::shared_ptr createMemento() const { + return std::make_shared(content, cursorPosition, selectionText); + } + + // Restore from memento + void restoreFromMemento(const std::shared_ptr& memento) { + content = memento->getContent(); + cursorPosition = memento->getCursorPosition(); + selectionText = memento->getSelectionText(); + } + + const std::string& getContent() const { return content; } + int getCursorPosition() const { return cursorPosition; } + const std::string& getSelectionText() const { return selectionText; } + + std::string toString() const { + return "Content: '" + content + "', Cursor: " + std::to_string(cursorPosition) + + ", Selection: '" + selectionText + "'"; + } +}; + +// Caretaker - manages mementos and implements undo/redo +class EditorHistory { +private: + std::stack> undoStack; + std::stack> redoStack; + +public: + void saveState(TextEditor& editor) { + undoStack.push(editor.createMemento()); + // Clear redo stack when new action is performed + while (!redoStack.empty()) { + redoStack.pop(); + } + } + + void undo(TextEditor& editor) { + if (!undoStack.empty()) { + redoStack.push(editor.createMemento()); + auto memento = undoStack.top(); + undoStack.pop(); + editor.restoreFromMemento(memento); + } + } + + void redo(TextEditor& editor) { + if (!redoStack.empty()) { + undoStack.push(editor.createMemento()); + auto memento = redoStack.top(); + redoStack.pop(); + editor.restoreFromMemento(memento); + } + } + + bool canUndo() const { return !undoStack.empty(); } + bool canRedo() const { return !redoStack.empty(); } +}; + +// Alternative example: Game State Management +struct GameStateMemento { + int level; + int score; + int health; + std::vector inventory; + + GameStateMemento(int l, int s, int h, const std::vector& inv) + : level(l), score(s), health(h), inventory(inv) {} +}; + +class GameState { +private: + int level; + int score; + int health; + std::vector inventory; + +public: + GameState() : level(1), score(0), health(100) {} + + void playLevel(int points, int healthLost, const std::vector& items) { + level++; + score += points; + health -= healthLost; + inventory.insert(inventory.end(), items.begin(), items.end()); + } + + std::shared_ptr createSavePoint() const { + return std::make_shared(level, score, health, inventory); + } + + void loadFromSavePoint(const std::shared_ptr& savePoint) { + level = savePoint->level; + score = savePoint->score; + health = savePoint->health; + inventory = savePoint->inventory; + } + + std::string toString() const { + std::string inventoryStr = ""; + for (size_t i = 0; i < inventory.size(); ++i) { + if (i > 0) inventoryStr += ", "; + inventoryStr += inventory[i]; + } + + return "Level: " + std::to_string(level) + + ", Score: " + std::to_string(score) + + ", Health: " + std::to_string(health) + + ", Inventory: [" + inventoryStr + "]"; + } + + int getHealth() const { return health; } +}; + +void demoTextEditor() { + std::cout << "=== Text Editor with Undo/Redo Demo ===\n\n"; + + TextEditor editor; + EditorHistory history; + + // Initial state + std::cout << "Initial: " << editor.toString() << std::endl; + + // First operation: write text + history.saveState(editor); + editor.write("Hello "); + std::cout << "After writing 'Hello ': " << editor.toString() << std::endl; + + // Second operation: write more text + history.saveState(editor); + editor.write("World!"); + std::cout << "After writing 'World!': " << editor.toString() << std::endl; + + // Third operation: move cursor and insert text + history.saveState(editor); + editor.setCursorPosition(6); + editor.write("Beautiful "); + std::cout << "After inserting 'Beautiful ': " << editor.toString() << std::endl; + + // Fourth operation: select text + history.saveState(editor); + editor.selectText(6, 15); + std::cout << "After selecting text: " << editor.toString() << std::endl; + + std::cout << "\n=== Undo Operations ===\n"; + + // Undo operations + history.undo(editor); + std::cout << "After undo 1: " << editor.toString() << std::endl; + + history.undo(editor); + std::cout << "After undo 2: " << editor.toString() << std::endl; + + history.undo(editor); + std::cout << "After undo 3: " << editor.toString() << std::endl; + + std::cout << "\n=== Redo Operations ===\n"; + + // Redo operations + history.redo(editor); + std::cout << "After redo 1: " << editor.toString() << std::endl; + + history.redo(editor); + std::cout << "After redo 2: " << editor.toString() << std::endl; + + // Demonstrate that new operations clear redo stack + std::cout << "\n=== New Operation Clears Redo Stack ===\n"; + editor.write(" How are you?"); + std::cout << "After new write operation: " << editor.toString() << std::endl; + std::cout << "Can redo: " << (history.canRedo() ? "true" : "false") << std::endl; +} + +void demoGameSaveSystem() { + std::cout << "\n\n=== Game Save System Demo ===\n\n"; + + GameState game; + std::vector> savePoints; + + std::cout << "Initial game state: " << game.toString() << std::endl; + + // Play level 1 + savePoints.push_back(game.createSavePoint()); + game.playLevel(100, 10, {"sword", "potion"}); + std::cout << "After level 2: " << game.toString() << std::endl; + + // Play level 2 + savePoints.push_back(game.createSavePoint()); + game.playLevel(150, 20, {"shield", "key"}); + std::cout << "After level 3: " << game.toString() << std::endl; + + // Play level 3 (difficult level) + savePoints.push_back(game.createSavePoint()); + game.playLevel(50, 80, {"gem"}); + std::cout << "After level 4 (tough level): " << game.toString() << std::endl; + + // Player health is low, load from previous save + std::cout << "\nPlayer health too low! Loading from save point..." << std::endl; + game.loadFromSavePoint(savePoints.back()); // Load from last save + std::cout << "After loading save: " << game.toString() << std::endl; + + // Try different strategy + game.playLevel(200, 30, {"magic_scroll", "armor"}); + std::cout << "After level 4 (better strategy): " << game.toString() << std::endl; +} + +int main() { + demoTextEditor(); + demoGameSaveSystem(); + return 0; +} diff --git a/src/content/design-pattern/code/memento/solution.java b/src/content/design-pattern/code/memento/solution.java new file mode 100644 index 0000000..beff40d --- /dev/null +++ b/src/content/design-pattern/code/memento/solution.java @@ -0,0 +1,172 @@ +import java.util.*; + +// Memento class - stores the internal state of TextEditor +class TextEditorMemento { + private final String content; + private final int cursorPosition; + private final String selectionText; + + public TextEditorMemento(String content, int cursorPosition, String selectionText) { + this.content = content; + this.cursorPosition = cursorPosition; + this.selectionText = selectionText; + } + + public String getContent() { return content; } + public int getCursorPosition() { return cursorPosition; } + public String getSelectionText() { return selectionText; } +} + +// Originator - creates and restores mementos +class TextEditor { + private StringBuilder content; + private int cursorPosition; + private String selectionText; + + public TextEditor() { + this.content = new StringBuilder(); + this.cursorPosition = 0; + this.selectionText = ""; + } + + public void write(String text) { + content.insert(cursorPosition, text); + cursorPosition += text.length(); + selectionText = ""; + } + + public void delete(int characters) { + int startPos = Math.max(0, cursorPosition - characters); + content.delete(startPos, cursorPosition); + cursorPosition = startPos; + selectionText = ""; + } + + public void setCursorPosition(int position) { + this.cursorPosition = Math.max(0, Math.min(position, content.length())); + } + + public void selectText(int startPos, int endPos) { + if (startPos >= 0 && endPos <= content.length() && startPos <= endPos) { + this.selectionText = content.substring(startPos, endPos); + } + } + + // Create memento + public TextEditorMemento createMemento() { + return new TextEditorMemento(content.toString(), cursorPosition, selectionText); + } + + // Restore from memento + public void restoreFromMemento(TextEditorMemento memento) { + this.content = new StringBuilder(memento.getContent()); + this.cursorPosition = memento.getCursorPosition(); + this.selectionText = memento.getSelectionText(); + } + + public String getContent() { return content.toString(); } + public int getCursorPosition() { return cursorPosition; } + public String getSelectionText() { return selectionText; } + + @Override + public String toString() { + return String.format("Content: '%s', Cursor: %d, Selection: '%s'", + content.toString(), cursorPosition, selectionText); + } +} + +// Caretaker - manages mementos and implements undo/redo +class EditorHistory { + private final Stack undoStack; + private final Stack redoStack; + + public EditorHistory() { + this.undoStack = new Stack<>(); + this.redoStack = new Stack<>(); + } + + public void saveState(TextEditor editor) { + undoStack.push(editor.createMemento()); + redoStack.clear(); // Clear redo stack when new action is performed + } + + public void undo(TextEditor editor) { + if (!undoStack.isEmpty()) { + redoStack.push(editor.createMemento()); + TextEditorMemento memento = undoStack.pop(); + editor.restoreFromMemento(memento); + } + } + + public void redo(TextEditor editor) { + if (!redoStack.isEmpty()) { + undoStack.push(editor.createMemento()); + TextEditorMemento memento = redoStack.pop(); + editor.restoreFromMemento(memento); + } + } + + public boolean canUndo() { return !undoStack.isEmpty(); } + public boolean canRedo() { return !redoStack.isEmpty(); } +} + +// Client code demonstrating the Memento pattern +public class MementoPatternDemo { + public static void main(String[] args) { + TextEditor editor = new TextEditor(); + EditorHistory history = new EditorHistory(); + + System.out.println("=== Text Editor with Undo/Redo Demo ===\n"); + + // Initial state + System.out.println("Initial: " + editor); + + // First operation: write text + history.saveState(editor); + editor.write("Hello "); + System.out.println("After writing 'Hello ': " + editor); + + // Second operation: write more text + history.saveState(editor); + editor.write("World!"); + System.out.println("After writing 'World!': " + editor); + + // Third operation: move cursor and insert text + history.saveState(editor); + editor.setCursorPosition(6); + editor.write("Beautiful "); + System.out.println("After inserting 'Beautiful ': " + editor); + + // Fourth operation: select text + history.saveState(editor); + editor.selectText(6, 15); + System.out.println("After selecting text: " + editor); + + System.out.println("\n=== Undo Operations ==="); + + // Undo operations + history.undo(editor); + System.out.println("After undo 1: " + editor); + + history.undo(editor); + System.out.println("After undo 2: " + editor); + + history.undo(editor); + System.out.println("After undo 3: " + editor); + + System.out.println("\n=== Redo Operations ==="); + + // Redo operations + history.redo(editor); + System.out.println("After redo 1: " + editor); + + history.redo(editor); + System.out.println("After redo 2: " + editor); + + // Demonstrate that new operations clear redo stack + System.out.println("\n=== New Operation Clears Redo Stack ==="); + editor.write(" How are you?"); + System.out.println("After new write operation: " + editor); + System.out.println("Can redo: " + history.canRedo()); + } +} diff --git a/src/content/design-pattern/code/memento/solution.py b/src/content/design-pattern/code/memento/solution.py new file mode 100644 index 0000000..f2907fc --- /dev/null +++ b/src/content/design-pattern/code/memento/solution.py @@ -0,0 +1,249 @@ +from typing import Optional, List + +class TextEditorMemento: + """Memento class that stores the internal state of TextEditor""" + + def __init__(self, content: str, cursor_position: int, selection_text: str): + self._content = content + self._cursor_position = cursor_position + self._selection_text = selection_text + + @property + def content(self) -> str: + return self._content + + @property + def cursor_position(self) -> int: + return self._cursor_position + + @property + def selection_text(self) -> str: + return self._selection_text + + +class TextEditor: + """Originator class that creates and restores mementos""" + + def __init__(self): + self._content = "" + self._cursor_position = 0 + self._selection_text = "" + + def write(self, text: str) -> None: + """Write text at current cursor position""" + self._content = (self._content[:self._cursor_position] + + text + + self._content[self._cursor_position:]) + self._cursor_position += len(text) + self._selection_text = "" + + def delete(self, characters: int) -> None: + """Delete specified number of characters before cursor""" + start_pos = max(0, self._cursor_position - characters) + self._content = (self._content[:start_pos] + + self._content[self._cursor_position:]) + self._cursor_position = start_pos + self._selection_text = "" + + def set_cursor_position(self, position: int) -> None: + """Set cursor position""" + self._cursor_position = max(0, min(position, len(self._content))) + + def select_text(self, start_pos: int, end_pos: int) -> None: + """Select text between start and end positions""" + if (0 <= start_pos <= end_pos <= len(self._content)): + self._selection_text = self._content[start_pos:end_pos] + + def create_memento(self) -> TextEditorMemento: + """Create memento with current state""" + return TextEditorMemento(self._content, self._cursor_position, self._selection_text) + + def restore_from_memento(self, memento: TextEditorMemento) -> None: + """Restore state from memento""" + self._content = memento.content + self._cursor_position = memento.cursor_position + self._selection_text = memento.selection_text + + @property + def content(self) -> str: + return self._content + + @property + def cursor_position(self) -> int: + return self._cursor_position + + @property + def selection_text(self) -> str: + return self._selection_text + + def __str__(self) -> str: + return f"Content: '{self._content}', Cursor: {self._cursor_position}, Selection: '{self._selection_text}'" + + +class EditorHistory: + """Caretaker class that manages mementos and implements undo/redo""" + + def __init__(self): + self._undo_stack: List[TextEditorMemento] = [] + self._redo_stack: List[TextEditorMemento] = [] + + def save_state(self, editor: TextEditor) -> None: + """Save current editor state""" + self._undo_stack.append(editor.create_memento()) + self._redo_stack.clear() # Clear redo stack when new action is performed + + def undo(self, editor: TextEditor) -> None: + """Undo last operation""" + if self._undo_stack: + self._redo_stack.append(editor.create_memento()) + memento = self._undo_stack.pop() + editor.restore_from_memento(memento) + + def redo(self, editor: TextEditor) -> None: + """Redo last undone operation""" + if self._redo_stack: + self._undo_stack.append(editor.create_memento()) + memento = self._redo_stack.pop() + editor.restore_from_memento(memento) + + def can_undo(self) -> bool: + """Check if undo is possible""" + return bool(self._undo_stack) + + def can_redo(self) -> bool: + """Check if redo is possible""" + return bool(self._redo_stack) + + +class GameState: + """Alternative example: Game state management""" + + def __init__(self): + self.level = 1 + self.score = 0 + self.health = 100 + self.inventory = [] + + def play_level(self, points: int, health_lost: int, items: List[str]) -> None: + """Simulate playing a level""" + self.level += 1 + self.score += points + self.health -= health_lost + self.inventory.extend(items) + + def create_save_point(self): + """Create a save point (memento)""" + return { + 'level': self.level, + 'score': self.score, + 'health': self.health, + 'inventory': self.inventory.copy() + } + + def load_from_save_point(self, save_point: dict) -> None: + """Load game state from save point""" + self.level = save_point['level'] + self.score = save_point['score'] + self.health = save_point['health'] + self.inventory = save_point['inventory'].copy() + + def __str__(self) -> str: + return f"Level: {self.level}, Score: {self.score}, Health: {self.health}, Inventory: {self.inventory}" + + +def demo_text_editor(): + """Demonstrate text editor with undo/redo functionality""" + editor = TextEditor() + history = EditorHistory() + + print("=== Text Editor with Undo/Redo Demo ===\n") + + # Initial state + print(f"Initial: {editor}") + + # First operation: write text + history.save_state(editor) + editor.write("Hello ") + print(f"After writing 'Hello ': {editor}") + + # Second operation: write more text + history.save_state(editor) + editor.write("World!") + print(f"After writing 'World!': {editor}") + + # Third operation: move cursor and insert text + history.save_state(editor) + editor.set_cursor_position(6) + editor.write("Beautiful ") + print(f"After inserting 'Beautiful ': {editor}") + + # Fourth operation: select text + history.save_state(editor) + editor.select_text(6, 15) + print(f"After selecting text: {editor}") + + print("\n=== Undo Operations ===") + + # Undo operations + history.undo(editor) + print(f"After undo 1: {editor}") + + history.undo(editor) + print(f"After undo 2: {editor}") + + history.undo(editor) + print(f"After undo 3: {editor}") + + print("\n=== Redo Operations ===") + + # Redo operations + history.redo(editor) + print(f"After redo 1: {editor}") + + history.redo(editor) + print(f"After redo 2: {editor}") + + # Demonstrate that new operations clear redo stack + print("\n=== New Operation Clears Redo Stack ===") + editor.write(" How are you?") + print(f"After new write operation: {editor}") + print(f"Can redo: {history.can_redo()}") + + +def demo_game_save_system(): + """Demonstrate game save system""" + print("\n\n=== Game Save System Demo ===\n") + + game = GameState() + save_points = [] + + print(f"Initial game state: {game}") + + # Play level 1 + save_points.append(game.create_save_point()) + game.play_level(100, 10, ["sword", "potion"]) + print(f"After level 2: {game}") + + # Play level 2 + save_points.append(game.create_save_point()) + game.play_level(150, 20, ["shield", "key"]) + print(f"After level 3: {game}") + + # Play level 3 (difficult level) + save_points.append(game.create_save_point()) + game.play_level(50, 80, ["gem"]) + print(f"After level 4 (tough level): {game}") + + # Player died, load from previous save + print(f"\nPlayer health too low! Loading from save point...") + game.load_from_save_point(save_points[-1]) # Load from last save + print(f"After loading save: {game}") + + # Try different strategy + game.play_level(200, 30, ["magic_scroll", "armor"]) + print(f"After level 4 (better strategy): {game}") + + +if __name__ == "__main__": + demo_text_editor() + demo_game_save_system() diff --git a/src/content/design-pattern/code/null-object/solution.java b/src/content/design-pattern/code/null-object/solution.java new file mode 100644 index 0000000..fcf446a --- /dev/null +++ b/src/content/design-pattern/code/null-object/solution.java @@ -0,0 +1,190 @@ +// Null Object Design Pattern - Logger System +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +// Abstract Logger interface +abstract class Logger { + public abstract void info(String message); + public abstract void warning(String message); + public abstract void error(String message); +} + +// Real Logger implementation +class ConsoleLogger extends Logger { + private String name; + + public ConsoleLogger(String name) { + this.name = name; + } + + @Override + public void info(String message) { + System.out.println("[INFO] " + getCurrentTime() + " [" + name + "] " + message); + } + + @Override + public void warning(String message) { + System.out.println("[WARN] " + getCurrentTime() + " [" + name + "] " + message); + } + + @Override + public void error(String message) { + System.err.println("[ERROR] " + getCurrentTime() + " [" + name + "] " + message); + } + + private String getCurrentTime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} + +// File Logger implementation +class FileLogger extends Logger { + private String filename; + + public FileLogger(String filename) { + this.filename = filename; + } + + @Override + public void info(String message) { + writeToFile("[INFO] " + getCurrentTime() + " " + message); + } + + @Override + public void warning(String message) { + writeToFile("[WARN] " + getCurrentTime() + " " + message); + } + + @Override + public void error(String message) { + writeToFile("[ERROR] " + getCurrentTime() + " " + message); + } + + private void writeToFile(String message) { + // Simulate file writing + System.out.println("Writing to " + filename + ": " + message); + } + + private String getCurrentTime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} + +// Null Object Logger - does nothing +class NullLogger extends Logger { + private static NullLogger instance = new NullLogger(); + + private NullLogger() {} // Private constructor for Singleton + + public static NullLogger getInstance() { + return instance; + } + + @Override + public void info(String message) { + // Do nothing - silent operation + } + + @Override + public void warning(String message) { + // Do nothing - silent operation + } + + @Override + public void error(String message) { + // Do nothing - silent operation + } +} + +// Service class that uses logger +class UserService { + private Logger logger; + + public UserService(Logger logger) { + this.logger = logger; + } + + public void createUser(String username) { + // Business logic + System.out.println("Creating user: " + username); + + // No null check needed - logger will handle it + logger.info("User created: " + username); + } + + public void deleteUser(String username) { + // Business logic + System.out.println("Deleting user: " + username); + + // No null check needed + logger.warning("User deleted: " + username); + } + + public void handleError(String error) { + // Error handling + System.out.println("Handling error: " + error); + + // No null check needed + logger.error("Error occurred: " + error); + } +} + +// Another service example +class OrderService { + private Logger logger; + + public OrderService(Logger logger) { + this.logger = logger; + } + + public void processOrder(String orderId) { + System.out.println("Processing order: " + orderId); + logger.info("Order processed: " + orderId); + + // Simulate some processing steps + logger.info("Validating payment for order: " + orderId); + logger.info("Updating inventory for order: " + orderId); + logger.info("Sending confirmation for order: " + orderId); + } +} + +// Client code +public class Main { + public static void main(String[] args) { + System.out.println("=== Null Object Pattern Demo ==="); + + // Service with real console logger + System.out.println("\n1. Using Console Logger:"); + UserService userServiceWithConsole = new UserService(new ConsoleLogger("UserService")); + userServiceWithConsole.createUser("john_doe"); + userServiceWithConsole.deleteUser("jane_smith"); + userServiceWithConsole.handleError("Database connection failed"); + + // Service with file logger + System.out.println("\n2. Using File Logger:"); + UserService userServiceWithFile = new UserService(new FileLogger("users.log")); + userServiceWithFile.createUser("alice"); + userServiceWithFile.deleteUser("bob"); + + // Service with null logger - no logging output + System.out.println("\n3. Using Null Logger (no log output):"); + UserService userServiceWithNull = new UserService(NullLogger.getInstance()); + userServiceWithNull.createUser("charlie"); + userServiceWithNull.deleteUser("diana"); + userServiceWithNull.handleError("Network timeout"); + + // Order service with different loggers + System.out.println("\n4. Order Service Examples:"); + + OrderService orderServiceWithConsole = new OrderService(new ConsoleLogger("OrderService")); + orderServiceWithConsole.processOrder("ORD-001"); + + System.out.println("\n Order Service with Null Logger:"); + OrderService orderServiceWithNull = new OrderService(NullLogger.getInstance()); + orderServiceWithNull.processOrder("ORD-002"); + + System.out.println("\n=== Demo Complete ==="); + System.out.println("Note: Null Object pattern eliminates the need for null checks"); + System.out.println("and provides consistent behavior regardless of logger availability."); + } +} diff --git a/src/content/design-pattern/code/object-pool/solution.cpp b/src/content/design-pattern/code/object-pool/solution.cpp new file mode 100644 index 0000000..3b05554 --- /dev/null +++ b/src/content/design-pattern/code/object-pool/solution.cpp @@ -0,0 +1,721 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Object Pool Pattern - Thread Pool Example + * Manages a pool of expensive worker threads for reuse and efficient task execution + */ + +// Forward declarations +class WorkerThread; + +// Poolable object interface +class PoolableObject { +public: + virtual ~PoolableObject() = default; + virtual void reset() = 0; + virtual bool isValid() const = 0; + virtual std::string getId() const = 0; + virtual std::chrono::time_point getCreatedAt() const = 0; + virtual std::chrono::time_point getLastUsedAt() const = 0; +}; + +// Task interface +class Task { +public: + virtual ~Task() = default; + virtual void execute() = 0; + virtual std::string getDescription() const = 0; +}; + +// Concrete task implementations +class ComputationTask : public Task { +private: + std::string taskId; + int iterations; + std::function callback; + +public: + ComputationTask(const std::string& id, int iter, std::function cb = nullptr) + : taskId(id), iterations(iter), callback(cb) {} + + void execute() override { + std::cout << "🧮 Executing computation task: " << taskId + << " (thread: " << std::this_thread::get_id() << ")" << std::endl; + + // Simulate computational work + long long result = 0; + for (int i = 0; i < iterations; ++i) { + result += i * i; + + // Add small delay to simulate work + if (i % 1000 == 0) { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + } + + std::cout << "✅ Completed computation task: " << taskId + << " (result: " << result << ")" << std::endl; + + if (callback) { + callback(taskId); + } + } + + std::string getDescription() const override { + return "ComputationTask[" + taskId + ", iterations=" + std::to_string(iterations) + "]"; + } +}; + +class IOTask : public Task { +private: + std::string taskId; + std::string filename; + +public: + IOTask(const std::string& id, const std::string& file) + : taskId(id), filename(file) {} + + void execute() override { + std::cout << "💾 Executing I/O task: " << taskId + << " (thread: " << std::this_thread::get_id() << ")" << std::endl; + + // Simulate I/O work + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::cout << "✅ Completed I/O task: " << taskId + << " (file: " << filename << ")" << std::endl; + } + + std::string getDescription() const override { + return "IOTask[" + taskId + ", file=" + filename + "]"; + } +}; + +// Worker thread class - poolable object +class WorkerThread : public PoolableObject { +private: + std::string threadId; + std::chrono::time_point createdAt; + std::chrono::time_point lastUsedAt; + std::atomic tasksExecuted{0}; + std::atomic busy{false}; + std::atomic shouldStop{false}; + + std::thread worker; + std::queue> taskQueue; + std::mutex taskMutex; + std::condition_variable taskCondition; + + static const int MAX_TASKS_PER_THREAD = 50; + static constexpr auto MAX_THREAD_AGE = std::chrono::minutes(5); + +public: + WorkerThread(const std::string& id) + : threadId(id), + createdAt(std::chrono::steady_clock::now()), + lastUsedAt(std::chrono::steady_clock::now()) { + + // Create worker thread + worker = std::thread(&WorkerThread::workerLoop, this); + + std::cout << "🧵 Created worker thread: " << threadId << std::endl; + + // Simulate expensive thread creation + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + ~WorkerThread() { + shutdown(); + } + + void reset() override { + lastUsedAt = std::chrono::steady_clock::now(); + busy = false; + std::cout << "🔄 Reset worker thread: " << threadId << std::endl; + } + + bool isValid() const override { + auto age = std::chrono::steady_clock::now() - createdAt; + + // Check age limit + if (age > MAX_THREAD_AGE) { + std::cout << "⏰ Worker thread " << threadId << " expired due to age" << std::endl; + return false; + } + + // Check task count limit + if (tasksExecuted.load() >= MAX_TASKS_PER_THREAD) { + std::cout << "🔢 Worker thread " << threadId << " expired due to task count: " + << tasksExecuted.load() << std::endl; + return false; + } + + // Check if thread is still running + if (shouldStop.load()) { + std::cout << "🛑 Worker thread " << threadId << " is stopping" << std::endl; + return false; + } + + return true; + } + + std::string getId() const override { + return threadId; + } + + std::chrono::time_point getCreatedAt() const override { + return createdAt; + } + + std::chrono::time_point getLastUsedAt() const override { + return lastUsedAt; + } + + bool isBusy() const { + return busy.load(); + } + + int getTasksExecuted() const { + return tasksExecuted.load(); + } + + void executeTask(std::unique_ptr task) { + { + std::lock_guard lock(taskMutex); + taskQueue.push(std::move(task)); + } + taskCondition.notify_one(); + } + + void shutdown() { + shouldStop = true; + taskCondition.notify_all(); + + if (worker.joinable()) { + worker.join(); + } + + std::cout << "🔚 Shutdown worker thread: " << threadId << std::endl; + } + + std::string getStatus() const { + auto age = std::chrono::steady_clock::now() - createdAt; + auto idle = std::chrono::steady_clock::now() - lastUsedAt; + + std::stringstream ss; + ss << "WorkerThread{id=" << threadId + << ", busy=" << (busy.load() ? "true" : "false") + << ", tasks=" << tasksExecuted.load() + << ", age=" << std::chrono::duration_cast(age).count() << "s" + << ", idle=" << std::chrono::duration_cast(idle).count() << "s" + << ", valid=" << (isValid() ? "true" : "false") << "}"; + + return ss.str(); + } + +private: + void workerLoop() { + while (!shouldStop.load()) { + std::unique_ptr task; + + { + std::unique_lock lock(taskMutex); + taskCondition.wait(lock, [this] { + return !taskQueue.empty() || shouldStop.load(); + }); + + if (shouldStop.load() && taskQueue.empty()) { + break; + } + + if (!taskQueue.empty()) { + task = std::move(taskQueue.front()); + taskQueue.pop(); + } + } + + if (task) { + busy = true; + lastUsedAt = std::chrono::steady_clock::now(); + + try { + task->execute(); + tasksExecuted++; + } catch (const std::exception& e) { + std::cerr << "❌ Task execution failed on " << threadId << ": " << e.what() << std::endl; + } + + busy = false; + } + } + + std::cout << "🔚 Worker thread loop ended: " << threadId << std::endl; + } +}; + +// Pool statistics +struct PoolStatistics { + size_t availableCount; + size_t inUseCount; + size_t totalCount; + size_t maxPoolSize; + size_t totalCreated; + size_t totalAcquired; + size_t totalReturned; + size_t totalExpired; + + double getUtilizationPercentage() const { + return maxPoolSize > 0 ? (totalCount * 100.0 / maxPoolSize) : 0.0; + } + + double getReusePercentage() const { + return totalAcquired > 0 ? (totalReturned * 100.0 / totalAcquired) : 0.0; + } + + std::string toString() const { + std::stringstream ss; + ss << "Pool Statistics:\n" + << "├─ Available: " << availableCount << "\n" + << "├─ In Use: " << inUseCount << "\n" + << "├─ Total: " << totalCount << "/" << maxPoolSize + << " (" << std::fixed << std::setprecision(1) << getUtilizationPercentage() << "% utilization)\n" + << "├─ Total Created: " << totalCreated << "\n" + << "├─ Total Acquired: " << totalAcquired << "\n" + << "├─ Total Returned: " << totalReturned << "\n" + << "├─ Total Expired: " << totalExpired << "\n" + << "└─ Reuse Rate: " << std::fixed << std::setprecision(1) << getReusePercentage() << "%"; + + return ss.str(); + } +}; + +// Generic object pool +template +class ObjectPool { +public: + using FactoryFunction = std::function()>; + +private: + FactoryFunction factory; + size_t minSize; + size_t maxSize; + + std::queue> available; + std::set inUse; + mutable std::mutex poolMutex; + std::condition_variable poolCondition; + + // Statistics + std::atomic totalCreated{0}; + std::atomic totalAcquired{0}; + std::atomic totalReturned{0}; + std::atomic totalExpired{0}; + + // Cleanup thread + std::thread cleanupThread; + std::atomic shouldShutdown{false}; + +public: + ObjectPool(FactoryFunction fact, size_t minSz, size_t maxSz) + : factory(fact), minSize(minSz), maxSize(maxSz) { + + // Initialize with minimum objects + initializePool(); + + // Start cleanup thread + cleanupThread = std::thread(&ObjectPool::cleanupLoop, this); + + std::cout << "🏊 ObjectPool initialized: min=" << minSize << ", max=" << maxSize << std::endl; + } + + ~ObjectPool() { + shutdown(); + } + + std::unique_ptr acquire(std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) { + std::unique_lock lock(poolMutex); + + auto deadline = std::chrono::steady_clock::now() + timeout; + + while (std::chrono::steady_clock::now() < deadline) { + // Try to get available object + if (!available.empty()) { + auto obj = std::move(available.front()); + available.pop(); + + if (obj->isValid()) { + inUse.insert(obj.get()); + totalAcquired++; + std::cout << "✅ Acquired object from pool: " << obj->getId() << std::endl; + return obj; + } else { + totalExpired++; + std::cout << "⏰ Object expired during acquire: " << obj->getId() << std::endl; + continue; + } + } + + // No available objects, try to create new one + if (getTotalCount() < maxSize) { + auto obj = factory(); + inUse.insert(obj.get()); + totalCreated++; + totalAcquired++; + std::cout << "🆕 Created new object for immediate use: " << obj->getId() << std::endl; + return obj; + } + + // Pool exhausted, wait + std::cout << "⏳ Pool exhausted, waiting for available object..." << std::endl; + poolCondition.wait_until(lock, deadline); + } + + throw std::runtime_error("Timeout waiting for object from pool"); + } + + void release(std::unique_ptr obj) { + if (!obj) return; + + std::lock_guard lock(poolMutex); + + auto it = inUse.find(obj.get()); + if (it != inUse.end()) { + inUse.erase(it); + + if (obj->isValid()) { + obj->reset(); + available.push(std::move(obj)); + totalReturned++; + std::cout << "🔄 Returned object to pool: " << (*it)->getId() << std::endl; + poolCondition.notify_one(); + } else { + totalExpired++; + std::cout << "⏰ Object expired on return: " << (*it)->getId() << std::endl; + } + } else { + std::cout << "⚠️ Attempted to return object not from this pool" << std::endl; + } + } + + PoolStatistics getStatistics() const { + std::lock_guard lock(poolMutex); + + return { + available.size(), + inUse.size(), + getTotalCount(), + maxSize, + totalCreated.load(), + totalAcquired.load(), + totalReturned.load(), + totalExpired.load() + }; + } + + void shutdown() { + shouldShutdown = true; + + if (cleanupThread.joinable()) { + cleanupThread.join(); + } + + std::lock_guard lock(poolMutex); + + // Shutdown all objects + while (!available.empty()) { + auto obj = std::move(available.front()); + available.pop(); + // obj will be automatically destroyed + } + + for (auto* objPtr : inUse) { + // Objects in use will be cleaned up when returned/destroyed + } + + std::cout << "🔚 ObjectPool shutdown completed" << std::endl; + } + +private: + void initializePool() { + std::lock_guard lock(poolMutex); + + for (size_t i = 0; i < minSize; ++i) { + auto obj = factory(); + available.push(std::move(obj)); + totalCreated++; + } + + std::cout << "📦 Pool initialized with " << minSize << " objects" << std::endl; + } + + void cleanupLoop() { + while (!shouldShutdown.load()) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + + if (!shouldShutdown.load()) { + cleanupExpiredObjects(); + ensureMinimumSize(); + } + } + } + + void cleanupExpiredObjects() { + std::lock_guard lock(poolMutex); + + std::queue> validObjects; + size_t expiredCount = 0; + + while (!available.empty()) { + auto obj = std::move(available.front()); + available.pop(); + + if (obj->isValid()) { + validObjects.push(std::move(obj)); + } else { + expiredCount++; + totalExpired++; + } + } + + available = std::move(validObjects); + + if (expiredCount > 0) { + std::cout << "🧹 Cleaned up " << expiredCount << " expired objects" << std::endl; + } + } + + void ensureMinimumSize() { + std::lock_guard lock(poolMutex); + + size_t currentAvailable = available.size(); + size_t currentTotal = getTotalCount(); + + if (currentAvailable < minSize && currentTotal < maxSize) { + size_t toCreate = std::min(minSize - currentAvailable, maxSize - currentTotal); + + for (size_t i = 0; i < toCreate; ++i) { + auto obj = factory(); + available.push(std::move(obj)); + totalCreated++; + } + + if (toCreate > 0) { + std::cout << "📈 Added " << toCreate << " objects to maintain minimum pool size" << std::endl; + } + } + } + + size_t getTotalCount() const { + return available.size() + inUse.size(); + } +}; + +// Thread pool service using object pool +class ThreadPoolService { +private: + ObjectPool threadPool; + std::atomic tasksSubmitted{0}; + std::atomic tasksCompleted{0}; + +public: + ThreadPoolService(size_t minThreads, size_t maxThreads) + : threadPool([this](){ + return std::make_unique("worker_" + std::to_string(tasksSubmitted.load())); + }, minThreads, maxThreads) { + + std::cout << "🏭 ThreadPoolService created with " << minThreads << "-" << maxThreads << " threads" << std::endl; + } + + void submitTask(std::unique_ptr task) { + try { + auto worker = threadPool.acquire(); + + std::cout << "📋 Submitting task: " << task->getDescription() + << " to worker: " << worker->getId() << std::endl; + + // Create callback to handle task completion + auto completionCallback = [this](const std::string& taskId) { + tasksCompleted++; + std::cout << "🎯 Task completed: " << taskId + << " (total completed: " << tasksCompleted.load() << ")" << std::endl; + }; + + tasksSubmitted++; + worker->executeTask(std::move(task)); + + // Return worker after small delay to simulate processing time + std::thread([this, worker = std::move(worker)]() mutable { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + threadPool.release(std::move(worker)); + }).detach(); + + } catch (const std::exception& e) { + std::cerr << "❌ Failed to submit task: " << e.what() << std::endl; + } + } + + std::future submitTaskAsync(std::unique_ptr task) { + return std::async(std::launch::async, [this, task = std::move(task)]() mutable { + submitTask(std::move(task)); + }); + } + + PoolStatistics getPoolStatistics() const { + return threadPool.getStatistics(); + } + + size_t getTasksSubmitted() const { return tasksSubmitted.load(); } + size_t getTasksCompleted() const { return tasksCompleted.load(); } + + void shutdown() { + std::cout << "🔚 Shutting down ThreadPoolService..." << std::endl; + + // Wait a bit for pending tasks + std::this_thread::sleep_for(std::chrono::seconds(1)); + + threadPool.shutdown(); + + std::cout << "📊 Final task statistics:" << std::endl; + std::cout << " Tasks submitted: " << tasksSubmitted.load() << std::endl; + std::cout << " Tasks completed: " << tasksCompleted.load() << std::endl; + } +}; + +int main() { + std::cout << "=== Object Pool Pattern Demo - Thread Pool ===\n" << std::endl; + + try { + // Create thread pool service + ThreadPoolService service(2, 6); + + std::cout << "\n1. Basic Thread Pool Operations:" << std::endl; + + // Submit some basic tasks + for (int i = 1; i <= 5; ++i) { + auto task = std::make_unique("compute_" + std::to_string(i), i * 10000); + service.submitTask(std::move(task)); + } + + // Wait a bit for tasks to process + std::this_thread::sleep_for(std::chrono::seconds(2)); + + std::cout << "\nPool statistics after basic operations:" << std::endl; + std::cout << service.getPoolStatistics().toString() << std::endl; + + std::cout << "\n2. Mixed Task Types:" << std::endl; + + // Submit mixed task types + for (int i = 1; i <= 3; ++i) { + auto computeTask = std::make_unique("mixed_compute_" + std::to_string(i), 50000); + auto ioTask = std::make_unique("mixed_io_" + std::to_string(i), "file_" + std::to_string(i) + ".txt"); + + service.submitTask(std::move(computeTask)); + service.submitTask(std::move(ioTask)); + } + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + std::cout << "\n3. Concurrent Task Submission:" << std::endl; + + // Submit tasks from multiple threads + std::vector> futures; + + for (int t = 0; t < 4; ++t) { + auto future = std::async(std::launch::async, [&service, t]() { + for (int i = 1; i <= 3; ++i) { + auto task = std::make_unique( + "thread_" + std::to_string(t) + "_task_" + std::to_string(i), + 25000 + ); + service.submitTask(std::move(task)); + + // Small delay between tasks + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + std::cout << "✅ Thread " << t << " finished submitting tasks" << std::endl; + }); + + futures.push_back(std::move(future)); + } + + // Wait for all submission threads + for (auto& future : futures) { + future.wait(); + } + + std::cout << "\n⏳ Waiting for all tasks to complete..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(3)); + + std::cout << "\n4. Final Statistics:" << std::endl; + auto stats = service.getPoolStatistics(); + std::cout << stats.toString() << std::endl; + + std::cout << "\nTask execution statistics:" << std::endl; + std::cout << "Tasks submitted: " << service.getTasksSubmitted() << std::endl; + std::cout << "Tasks completed: " << service.getTasksCompleted() << std::endl; + + std::cout << "\n5. Pool Efficiency Analysis:" << std::endl; + std::cout << "Pool utilization: " << std::fixed << std::setprecision(1) + << stats.getUtilizationPercentage() << "%" << std::endl; + std::cout << "Thread reuse rate: " << std::fixed << std::setprecision(1) + << stats.getReusePercentage() << "%" << std::endl; + + if (stats.totalCreated > 0) { + double tasksPerThread = static_cast(service.getTasksSubmitted()) / stats.totalCreated; + std::cout << "Average tasks per thread: " << std::fixed << std::setprecision(1) + << tasksPerThread << std::endl; + } + + std::cout << "\n6. Pool Stress Test:" << std::endl; + + // Rapid task submission to test pool limits + auto startTime = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < 20; ++i) { + auto task = std::make_unique("stress_" + std::to_string(i), 10000); + service.submitTask(std::move(task)); + } + + auto submitTime = std::chrono::high_resolution_clock::now(); + auto submitDuration = std::chrono::duration_cast(submitTime - startTime); + + std::cout << "📊 Submitted 20 tasks in " << submitDuration.count() << "ms" << std::endl; + + // Wait for completion + std::this_thread::sleep_for(std::chrono::seconds(2)); + + auto endTime = std::chrono::high_resolution_clock::now(); + auto totalDuration = std::chrono::duration_cast(endTime - startTime); + + std::cout << "⏱️ Total stress test duration: " << totalDuration.count() << "ms" << std::endl; + + std::cout << "\nFinal pool statistics:" << std::endl; + std::cout << service.getPoolStatistics().toString() << std::endl; + + // Shutdown + std::cout << "\n7. Service Shutdown:" << std::endl; + service.shutdown(); + + std::cout << "\n✅ Object Pool pattern successfully demonstrated!" << std::endl; + std::cout << "Benefits: Thread reuse, resource management, performance optimization, controlled concurrency" << std::endl; + + } catch (const std::exception& e) { + std::cerr << "❌ Demo failed: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/content/design-pattern/code/object-pool/solution.java b/src/content/design-pattern/code/object-pool/solution.java new file mode 100644 index 0000000..31fc07b --- /dev/null +++ b/src/content/design-pattern/code/object-pool/solution.java @@ -0,0 +1,630 @@ +/** + * Object Pool Pattern - Database Connection Pool Example + * Manages a pool of expensive-to-create objects for reuse, improving performance + */ + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReentrantLock; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +// Poolable object interface +interface PoolableObject { + void reset(); + boolean isValid(); + String getId(); + LocalDateTime getCreatedAt(); + LocalDateTime getLastUsedAt(); +} + +// Concrete poolable object - Database Connection +class DatabaseConnection implements PoolableObject { + private final String connectionId; + private final String connectionString; + private final LocalDateTime createdAt; + private LocalDateTime lastUsedAt; + private boolean connected; + private int queryCount; + private String currentDatabase; + + public DatabaseConnection(String connectionId, String host, int port, String database) { + this.connectionId = connectionId; + this.connectionString = String.format("jdbc:postgresql://%s:%d/%s", host, port, database); + this.createdAt = LocalDateTime.now(); + this.lastUsedAt = LocalDateTime.now(); + this.connected = false; + this.queryCount = 0; + this.currentDatabase = database; + + // Simulate expensive connection creation + try { + Thread.sleep(100); // Simulate network delay + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + System.out.println("🔗 Created new database connection: " + connectionId); + } + + @Override + public void reset() { + this.lastUsedAt = LocalDateTime.now(); + this.connected = false; + this.queryCount = 0; + System.out.println("🔄 Reset connection: " + connectionId); + } + + @Override + public boolean isValid() { + // Simulate connection validation + long minutesSinceCreation = java.time.Duration.between(createdAt, LocalDateTime.now()).toMinutes(); + return minutesSinceCreation < 30; // Connection expires after 30 minutes + } + + @Override + public String getId() { + return connectionId; + } + + @Override + public LocalDateTime getCreatedAt() { + return createdAt; + } + + @Override + public LocalDateTime getLastUsedAt() { + return lastUsedAt; + } + + // Database-specific methods + public void connect() { + if (!connected) { + connected = true; + lastUsedAt = LocalDateTime.now(); + System.out.println("📡 Connected to database via " + connectionId); + } + } + + public void disconnect() { + if (connected) { + connected = false; + System.out.println("📡 Disconnected from database via " + connectionId); + } + } + + public void executeQuery(String query) { + if (!connected) { + throw new IllegalStateException("Connection not established"); + } + + queryCount++; + lastUsedAt = LocalDateTime.now(); + System.out.println("🔍 Executing query on " + connectionId + ": " + query); + + // Simulate query execution time + try { + Thread.sleep(50); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public String getConnectionString() { + return connectionString; + } + + public boolean isConnected() { + return connected; + } + + public int getQueryCount() { + return queryCount; + } + + public String getStatus() { + return String.format("Connection{id=%s, connected=%s, queries=%d, created=%s, lastUsed=%s}", + connectionId, connected, queryCount, + createdAt.format(DateTimeFormatter.ofPattern("HH:mm:ss")), + lastUsedAt.format(DateTimeFormatter.ofPattern("HH:mm:ss"))); + } +} + +// Generic Object Pool implementation +class ObjectPool { + private final Queue availableObjects; + private final Set inUseObjects; + private final int maxPoolSize; + private final int minPoolSize; + private final ReentrantLock lock; + private final ObjectFactory factory; + private final ScheduledExecutorService cleanupExecutor; + + // Statistics + private int totalCreated; + private int totalAcquired; + private int totalReturned; + private int totalExpired; + + public interface ObjectFactory { + T createObject(); + } + + public ObjectPool(ObjectFactory factory, int minPoolSize, int maxPoolSize) { + this.factory = factory; + this.minPoolSize = minPoolSize; + this.maxPoolSize = maxPoolSize; + this.availableObjects = new LinkedList<>(); + this.inUseObjects = new HashSet<>(); + this.lock = new ReentrantLock(); + this.cleanupExecutor = Executors.newSingleThreadScheduledExecutor(); + + this.totalCreated = 0; + this.totalAcquired = 0; + this.totalReturned = 0; + this.totalExpired = 0; + + // Initialize with minimum objects + initializePool(); + + // Schedule periodic cleanup + scheduleCleanup(); + + System.out.println("🏊 Object Pool initialized: min=" + minPoolSize + ", max=" + maxPoolSize); + } + + private void initializePool() { + lock.lock(); + try { + for (int i = 0; i < minPoolSize; i++) { + T obj = factory.createObject(); + availableObjects.offer(obj); + totalCreated++; + } + System.out.println("🏊 Pool initialized with " + minPoolSize + " objects"); + } finally { + lock.unlock(); + } + } + + public T acquire() throws InterruptedException { + return acquire(5, TimeUnit.SECONDS); + } + + public T acquire(long timeout, TimeUnit unit) throws InterruptedException { + long timeoutMillis = unit.toMillis(timeout); + long startTime = System.currentTimeMillis(); + + lock.lock(); + try { + while (true) { + // Try to get an available object + T obj = availableObjects.poll(); + + if (obj != null) { + if (obj.isValid()) { + inUseObjects.add(obj); + totalAcquired++; + System.out.println("✅ Acquired object from pool: " + obj.getId()); + return obj; + } else { + // Object expired, don't return it to pool + totalExpired++; + System.out.println("⏰ Object expired and removed: " + obj.getId()); + continue; + } + } + + // No available objects, try to create new one + if (getTotalObjects() < maxPoolSize) { + obj = factory.createObject(); + inUseObjects.add(obj); + totalCreated++; + totalAcquired++; + System.out.println("🆕 Created new object for immediate use: " + obj.getId()); + return obj; + } + + // Pool is full, wait for an object to be returned + long elapsed = System.currentTimeMillis() - startTime; + if (elapsed >= timeoutMillis) { + throw new RuntimeException("Timeout waiting for object from pool"); + } + + System.out.println("⏳ Pool exhausted, waiting for available object..."); + Thread.sleep(100); // Wait a bit before retrying + } + } finally { + lock.unlock(); + } + } + + public void release(T obj) { + if (obj == null) { + return; + } + + lock.lock(); + try { + if (inUseObjects.remove(obj)) { + if (obj.isValid()) { + obj.reset(); + availableObjects.offer(obj); + totalReturned++; + System.out.println("🔄 Returned object to pool: " + obj.getId()); + } else { + totalExpired++; + System.out.println("⏰ Object expired on return: " + obj.getId()); + } + } else { + System.out.println("⚠️ Attempted to return object not from this pool: " + obj.getId()); + } + } finally { + lock.unlock(); + } + } + + private void scheduleCleanup() { + cleanupExecutor.scheduleAtFixedRate(() -> { + cleanupExpiredObjects(); + ensureMinimumSize(); + }, 10, 10, TimeUnit.SECONDS); + } + + private void cleanupExpiredObjects() { + lock.lock(); + try { + Iterator iterator = availableObjects.iterator(); + int removedCount = 0; + + while (iterator.hasNext()) { + T obj = iterator.next(); + if (!obj.isValid()) { + iterator.remove(); + removedCount++; + totalExpired++; + } + } + + if (removedCount > 0) { + System.out.println("🧹 Cleaned up " + removedCount + " expired objects from pool"); + } + } finally { + lock.unlock(); + } + } + + private void ensureMinimumSize() { + lock.lock(); + try { + int currentAvailable = availableObjects.size(); + int totalCurrent = getTotalObjects(); + + if (currentAvailable < minPoolSize && totalCurrent < maxPoolSize) { + int toCreate = Math.min(minPoolSize - currentAvailable, maxPoolSize - totalCurrent); + + for (int i = 0; i < toCreate; i++) { + T obj = factory.createObject(); + availableObjects.offer(obj); + totalCreated++; + } + + if (toCreate > 0) { + System.out.println("📈 Added " + toCreate + " objects to maintain minimum pool size"); + } + } + } finally { + lock.unlock(); + } + } + + public PoolStatistics getStatistics() { + lock.lock(); + try { + return new PoolStatistics( + availableObjects.size(), + inUseObjects.size(), + getTotalObjects(), + maxPoolSize, + totalCreated, + totalAcquired, + totalReturned, + totalExpired + ); + } finally { + lock.unlock(); + } + } + + private int getTotalObjects() { + return availableObjects.size() + inUseObjects.size(); + } + + public void shutdown() { + cleanupExecutor.shutdown(); + try { + if (!cleanupExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + cleanupExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + cleanupExecutor.shutdownNow(); + } + + lock.lock(); + try { + System.out.println("🔚 Pool shutdown. Final statistics:"); + System.out.println(getStatistics()); + } finally { + lock.unlock(); + } + } + + // Statistics class + public static class PoolStatistics { + private final int availableCount; + private final int inUseCount; + private final int totalCount; + private final int maxPoolSize; + private final int totalCreated; + private final int totalAcquired; + private final int totalReturned; + private final int totalExpired; + + public PoolStatistics(int availableCount, int inUseCount, int totalCount, int maxPoolSize, + int totalCreated, int totalAcquired, int totalReturned, int totalExpired) { + this.availableCount = availableCount; + this.inUseCount = inUseCount; + this.totalCount = totalCount; + this.maxPoolSize = maxPoolSize; + this.totalCreated = totalCreated; + this.totalAcquired = totalAcquired; + this.totalReturned = totalReturned; + this.totalExpired = totalExpired; + } + + @Override + public String toString() { + return String.format(""" + Pool Statistics: + ├─ Available: %d + ├─ In Use: %d + ├─ Total: %d/%d (%.1f%% utilization) + ├─ Total Created: %d + ├─ Total Acquired: %d + ├─ Total Returned: %d + ├─ Total Expired: %d + └─ Efficiency: %.1f%% (reuse rate) + """, + availableCount, inUseCount, totalCount, maxPoolSize, + (totalCount * 100.0 / maxPoolSize), + totalCreated, totalAcquired, totalReturned, totalExpired, + totalAcquired > 0 ? (totalReturned * 100.0 / totalAcquired) : 0.0 + ); + } + + // Getters + public int getAvailableCount() { return availableCount; } + public int getInUseCount() { return inUseCount; } + public int getTotalCount() { return totalCount; } + public double getUtilizationPercentage() { return totalCount * 100.0 / maxPoolSize; } + } +} + +// Example client class using the pool +class DatabaseService { + private final ObjectPool connectionPool; + + public DatabaseService(ObjectPool connectionPool) { + this.connectionPool = connectionPool; + } + + public void performDatabaseOperation(String operation) { + DatabaseConnection connection = null; + + try { + System.out.println("🔄 Starting database operation: " + operation); + connection = connectionPool.acquire(); + + connection.connect(); + connection.executeQuery("SELECT * FROM users WHERE operation = '" + operation + "'"); + connection.executeQuery("UPDATE statistics SET count = count + 1 WHERE operation = '" + operation + "'"); + connection.disconnect(); + + System.out.println("✅ Completed database operation: " + operation); + + } catch (Exception e) { + System.err.println("❌ Database operation failed: " + e.getMessage()); + } finally { + if (connection != null) { + connectionPool.release(connection); + } + } + } + + public void performBatchOperations(List operations) { + System.out.println("📦 Starting batch operations (" + operations.size() + " operations)"); + + for (String operation : operations) { + performDatabaseOperation(operation); + } + + System.out.println("✅ Completed batch operations"); + } +} + +// Worker thread for testing concurrent access +class DatabaseWorker implements Runnable { + private final DatabaseService dbService; + private final String workerName; + private final int operationCount; + + public DatabaseWorker(DatabaseService dbService, String workerName, int operationCount) { + this.dbService = dbService; + this.workerName = workerName; + this.operationCount = operationCount; + } + + @Override + public void run() { + System.out.println("🏃 " + workerName + " started"); + + List operations = new ArrayList<>(); + for (int i = 1; i <= operationCount; i++) { + operations.add(workerName + "_operation_" + i); + } + + dbService.performBatchOperations(operations); + + System.out.println("✅ " + workerName + " completed"); + } +} + +public class ObjectPoolPatternDemo { + public static void main(String[] args) { + System.out.println("=== Object Pool Pattern Demo - Database Connection Pool ===\n"); + + // Create connection pool with factory + ObjectPool.ObjectFactory connectionFactory = () -> { + String connectionId = "conn_" + System.currentTimeMillis() + "_" + + (int)(Math.random() * 1000); + return new DatabaseConnection(connectionId, "localhost", 5432, "testdb"); + }; + + ObjectPool connectionPool = new ObjectPool<>(connectionFactory, 3, 8); + + System.out.println("\n1. Basic Pool Operations:"); + + try { + // Test basic acquire and release + DatabaseConnection conn1 = connectionPool.acquire(); + DatabaseConnection conn2 = connectionPool.acquire(); + + System.out.println("Acquired connections:"); + System.out.println(" " + conn1.getStatus()); + System.out.println(" " + conn2.getStatus()); + + System.out.println("\nPool statistics after acquisition:"); + System.out.println(connectionPool.getStatistics()); + + // Use the connections + conn1.connect(); + conn1.executeQuery("SELECT 1"); + conn1.disconnect(); + + conn2.connect(); + conn2.executeQuery("SELECT COUNT(*) FROM users"); + conn2.disconnect(); + + // Return connections to pool + connectionPool.release(conn1); + connectionPool.release(conn2); + + System.out.println("Pool statistics after release:"); + System.out.println(connectionPool.getStatistics()); + + } catch (InterruptedException e) { + System.err.println("Interrupted while acquiring connection: " + e.getMessage()); + } + + System.out.println("\n2. Database Service Usage:"); + + // Create database service using the pool + DatabaseService dbService = new DatabaseService(connectionPool); + + // Perform some database operations + dbService.performDatabaseOperation("user_login"); + dbService.performDatabaseOperation("data_export"); + dbService.performDatabaseOperation("report_generation"); + + System.out.println("\nPool statistics after service operations:"); + System.out.println(connectionPool.getStatistics()); + + System.out.println("\n3. Concurrent Access Testing:"); + + // Test with multiple threads + ExecutorService executor = Executors.newFixedThreadPool(5); + + long startTime = System.currentTimeMillis(); + + // Submit multiple workers + for (int i = 1; i <= 4; i++) { + executor.submit(new DatabaseWorker(dbService, "Worker-" + i, 3)); + } + + // Wait for completion + executor.shutdown(); + try { + if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + } + + long endTime = System.currentTimeMillis(); + + System.out.println("\n⏱️ Concurrent operations completed in " + (endTime - startTime) + "ms"); + + System.out.println("\n4. Final Pool Statistics:"); + ObjectPool.PoolStatistics stats = connectionPool.getStatistics(); + System.out.println(stats); + + System.out.println("\n5. Pool Utilization Analysis:"); + System.out.printf("Pool Efficiency: %.1f%% utilization\n", stats.getUtilizationPercentage()); + System.out.printf("Object Reuse Rate: %.1f%%\n", + stats.getInUseCount() > 0 ? (stats.getTotalCount() * 100.0 / stats.getInUseCount()) : 100.0); + + // Test pool exhaustion + System.out.println("\n6. Testing Pool Exhaustion:"); + List heldConnections = new ArrayList<>(); + + try { + // Acquire all available connections + for (int i = 0; i < 8; i++) { + DatabaseConnection conn = connectionPool.acquire(); + heldConnections.add(conn); + System.out.println("Acquired connection " + (i + 1) + "/8: " + conn.getId()); + } + + System.out.println("Pool is now exhausted:"); + System.out.println(connectionPool.getStatistics()); + + // Try to acquire one more (should timeout) + try { + DatabaseConnection extraConn = connectionPool.acquire(2, TimeUnit.SECONDS); + System.out.println("ERROR: Should not have acquired extra connection!"); + connectionPool.release(extraConn); + } catch (RuntimeException e) { + System.out.println("✅ Expected timeout: " + e.getMessage()); + } + + } catch (InterruptedException e) { + System.err.println("Interrupted during exhaustion test: " + e.getMessage()); + } finally { + // Release all held connections + for (DatabaseConnection conn : heldConnections) { + connectionPool.release(conn); + } + } + + System.out.println("\n7. Pool After Releasing All Connections:"); + System.out.println(connectionPool.getStatistics()); + + // Wait a bit to see cleanup in action + System.out.println("\n8. Testing Automatic Cleanup (waiting 15 seconds)..."); + try { + Thread.sleep(15000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + System.out.println("Pool statistics after cleanup period:"); + System.out.println(connectionPool.getStatistics()); + + // Shutdown pool + connectionPool.shutdown(); + + System.out.println("\n✅ Object Pool pattern successfully demonstrated!"); + System.out.println("Benefits: Resource reuse, performance optimization, controlled resource allocation, automatic cleanup"); + } +} diff --git a/src/content/design-pattern/code/object-pool/solution.py b/src/content/design-pattern/code/object-pool/solution.py new file mode 100644 index 0000000..fbf428b --- /dev/null +++ b/src/content/design-pattern/code/object-pool/solution.py @@ -0,0 +1,674 @@ +""" +Object Pool Pattern - HTTP Connection Pool Example +Manages a pool of expensive HTTP connections for reuse, improving performance and resource management +""" + +import threading +import time +import queue +from abc import ABC, abstractmethod +from datetime import datetime, timedelta +from typing import Optional, Dict, Any, List +from dataclasses import dataclass +import requests +from concurrent.futures import ThreadPoolExecutor, as_completed +import weakref + +class PoolableObject(ABC): + """Abstract base class for objects that can be pooled""" + + @abstractmethod + def reset(self) -> None: + """Reset object state for reuse""" + pass + + @abstractmethod + def is_valid(self) -> bool: + """Check if object is still valid for use""" + pass + + @abstractmethod + def get_id(self) -> str: + """Get unique identifier for this object""" + pass + +class HTTPConnection(PoolableObject): + """Poolable HTTP connection object""" + + def __init__(self, connection_id: str, base_url: str, timeout: int = 30): + self.connection_id = connection_id + self.base_url = base_url + self.timeout = timeout + self.created_at = datetime.now() + self.last_used_at = datetime.now() + self.request_count = 0 + self.session = None + self.max_age = timedelta(minutes=10) # Connection expires after 10 minutes + self.max_requests = 100 # Connection expires after 100 requests + + self._initialize_session() + print(f"🔗 Created new HTTP connection: {self.connection_id} -> {self.base_url}") + + # Simulate expensive connection setup + time.sleep(0.1) + + def _initialize_session(self): + """Initialize requests session with connection pooling""" + self.session = requests.Session() + + # Configure session with connection pooling + adapter = requests.adapters.HTTPAdapter( + pool_connections=1, + pool_maxsize=1, + max_retries=3 + ) + self.session.mount('http://', adapter) + self.session.mount('https://', adapter) + + # Set default headers + self.session.headers.update({ + 'User-Agent': f'PooledHTTPClient/{self.connection_id}', + 'Connection': 'keep-alive' + }) + + def reset(self) -> None: + """Reset connection for reuse""" + self.last_used_at = datetime.now() + + # Clear any session cookies or temporary state + if self.session: + self.session.cookies.clear() + + print(f"🔄 Reset HTTP connection: {self.connection_id}") + + def is_valid(self) -> bool: + """Check if connection is still valid""" + age = datetime.now() - self.created_at + + # Check age limit + if age > self.max_age: + print(f"⏰ Connection {self.connection_id} expired due to age: {age}") + return False + + # Check request count limit + if self.request_count >= self.max_requests: + print(f"🔢 Connection {self.connection_id} expired due to request count: {self.request_count}") + return False + + # Check if session is still alive + if not self.session: + print(f"💀 Connection {self.connection_id} has no active session") + return False + + return True + + def get_id(self) -> str: + return self.connection_id + + def get(self, endpoint: str, **kwargs) -> requests.Response: + """Perform GET request""" + return self._request('GET', endpoint, **kwargs) + + def post(self, endpoint: str, **kwargs) -> requests.Response: + """Perform POST request""" + return self._request('POST', endpoint, **kwargs) + + def put(self, endpoint: str, **kwargs) -> requests.Response: + """Perform PUT request""" + return self._request('PUT', endpoint, **kwargs) + + def delete(self, endpoint: str, **kwargs) -> requests.Response: + """Perform DELETE request""" + return self._request('DELETE', endpoint, **kwargs) + + def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response: + """Perform HTTP request""" + if not self.is_valid(): + raise ValueError(f"Connection {self.connection_id} is not valid") + + url = f"{self.base_url.rstrip('/')}/{endpoint.lstrip('/')}" + self.last_used_at = datetime.now() + self.request_count += 1 + + print(f"🌐 {method} request via {self.connection_id}: {endpoint}") + + # Set timeout if not provided + if 'timeout' not in kwargs: + kwargs['timeout'] = self.timeout + + try: + response = self.session.request(method, url, **kwargs) + print(f"✅ Request completed: {response.status_code} from {self.connection_id}") + return response + except Exception as e: + print(f"❌ Request failed on {self.connection_id}: {e}") + raise + + def close(self): + """Close the connection""" + if self.session: + self.session.close() + self.session = None + print(f"🔌 Closed HTTP connection: {self.connection_id}") + + def get_status(self) -> Dict[str, Any]: + """Get connection status information""" + age = datetime.now() - self.created_at + idle_time = datetime.now() - self.last_used_at + + return { + 'id': self.connection_id, + 'base_url': self.base_url, + 'request_count': self.request_count, + 'age_seconds': int(age.total_seconds()), + 'idle_seconds': int(idle_time.total_seconds()), + 'is_valid': self.is_valid(), + 'created_at': self.created_at.strftime('%H:%M:%S'), + 'last_used_at': self.last_used_at.strftime('%H:%M:%S') + } + +@dataclass +class PoolStatistics: + """Statistics for object pool""" + available_count: int + in_use_count: int + total_count: int + max_pool_size: int + total_created: int + total_acquired: int + total_returned: int + total_expired: int + + @property + def utilization_percentage(self) -> float: + return (self.total_count / self.max_pool_size) * 100 if self.max_pool_size > 0 else 0 + + @property + def reuse_percentage(self) -> float: + return (self.total_returned / self.total_acquired) * 100 if self.total_acquired > 0 else 0 + + def __str__(self) -> str: + return f"""Pool Statistics: +├─ Available: {self.available_count} +├─ In Use: {self.in_use_count} +├─ Total: {self.total_count}/{self.max_pool_size} ({self.utilization_percentage:.1f}% utilization) +├─ Total Created: {self.total_created} +├─ Total Acquired: {self.total_acquired} +├─ Total Returned: {self.total_returned} +├─ Total Expired: {self.total_expired} +└─ Reuse Rate: {self.reuse_percentage:.1f}%""" + +class ObjectPool: + """Generic object pool implementation""" + + def __init__(self, factory, min_size: int = 2, max_size: int = 10, cleanup_interval: int = 30): + self.factory = factory + self.min_size = min_size + self.max_size = max_size + self.cleanup_interval = cleanup_interval + + # Pool management + self._available = queue.Queue() + self._in_use = set() + self._lock = threading.RLock() + self._condition = threading.Condition(self._lock) + + # Statistics + self._total_created = 0 + self._total_acquired = 0 + self._total_returned = 0 + self._total_expired = 0 + + # Cleanup thread + self._cleanup_thread = None + self._shutdown = False + + # Initialize pool + self._initialize_pool() + self._start_cleanup_thread() + + print(f"🏊 Object Pool initialized: min={min_size}, max={max_size}") + + def _initialize_pool(self): + """Initialize pool with minimum number of objects""" + for _ in range(self.min_size): + obj = self.factory() + self._available.put(obj) + self._total_created += 1 + + print(f"📦 Pool initialized with {self.min_size} objects") + + def _start_cleanup_thread(self): + """Start background cleanup thread""" + self._cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True) + self._cleanup_thread.start() + + def _cleanup_loop(self): + """Background cleanup loop""" + while not self._shutdown: + try: + time.sleep(self.cleanup_interval) + if not self._shutdown: + self._cleanup_expired_objects() + self._ensure_minimum_size() + except Exception as e: + print(f"⚠️ Cleanup error: {e}") + + def _cleanup_expired_objects(self): + """Remove expired objects from the pool""" + with self._lock: + expired_objects = [] + + # Check available objects + temp_queue = queue.Queue() + while not self._available.empty(): + try: + obj = self._available.get_nowait() + if obj.is_valid(): + temp_queue.put(obj) + else: + expired_objects.append(obj) + self._total_expired += 1 + except queue.Empty: + break + + # Put back valid objects + while not temp_queue.empty(): + self._available.put(temp_queue.get()) + + # Close expired objects + for obj in expired_objects: + if hasattr(obj, 'close'): + obj.close() + + if expired_objects: + print(f"🧹 Cleaned up {len(expired_objects)} expired objects") + + def _ensure_minimum_size(self): + """Ensure pool has minimum number of objects""" + with self._lock: + current_total = self._available.qsize() + len(self._in_use) + current_available = self._available.qsize() + + if current_available < self.min_size and current_total < self.max_size: + to_create = min( + self.min_size - current_available, + self.max_size - current_total + ) + + for _ in range(to_create): + try: + obj = self.factory() + self._available.put(obj) + self._total_created += 1 + except Exception as e: + print(f"❌ Error creating object during maintenance: {e}") + break + + if to_create > 0: + print(f"📈 Added {to_create} objects to maintain minimum pool size") + + def acquire(self, timeout: Optional[float] = None) -> PoolableObject: + """Acquire an object from the pool""" + start_time = time.time() + + with self._condition: + while True: + # Try to get an available object + try: + obj = self._available.get_nowait() + if obj.is_valid(): + self._in_use.add(obj) + self._total_acquired += 1 + print(f"✅ Acquired object from pool: {obj.get_id()}") + return obj + else: + # Object expired + self._total_expired += 1 + print(f"⏰ Object expired during acquire: {obj.get_id()}") + if hasattr(obj, 'close'): + obj.close() + continue + except queue.Empty: + pass + + # No available objects, try to create new one + current_total = self._available.qsize() + len(self._in_use) + if current_total < self.max_size: + try: + obj = self.factory() + self._in_use.add(obj) + self._total_created += 1 + self._total_acquired += 1 + print(f"🆕 Created new object for immediate use: {obj.get_id()}") + return obj + except Exception as e: + print(f"❌ Error creating new object: {e}") + raise + + # Pool exhausted, wait or timeout + if timeout is not None: + elapsed = time.time() - start_time + remaining = timeout - elapsed + if remaining <= 0: + raise TimeoutError("Timeout waiting for object from pool") + + print(f"⏳ Pool exhausted, waiting up to {remaining:.1f}s for available object...") + if not self._condition.wait(min(remaining, 1.0)): + continue # Timeout on wait, check again + else: + print("⏳ Pool exhausted, waiting for available object...") + self._condition.wait(1.0) # Wait 1 second then retry + + def release(self, obj: PoolableObject) -> None: + """Return an object to the pool""" + if obj is None: + return + + with self._lock: + if obj in self._in_use: + self._in_use.remove(obj) + + if obj.is_valid(): + obj.reset() + self._available.put(obj) + self._total_returned += 1 + print(f"🔄 Returned object to pool: {obj.get_id()}") + self._condition.notify() # Notify waiting threads + else: + self._total_expired += 1 + print(f"⏰ Object expired on return: {obj.get_id()}") + if hasattr(obj, 'close'): + obj.close() + else: + print(f"⚠️ Attempted to return object not from this pool: {obj.get_id()}") + + def get_statistics(self) -> PoolStatistics: + """Get current pool statistics""" + with self._lock: + return PoolStatistics( + available_count=self._available.qsize(), + in_use_count=len(self._in_use), + total_count=self._available.qsize() + len(self._in_use), + max_pool_size=self.max_size, + total_created=self._total_created, + total_acquired=self._total_acquired, + total_returned=self._total_returned, + total_expired=self._total_expired + ) + + def shutdown(self): + """Shutdown the pool and cleanup resources""" + print("🔚 Shutting down object pool...") + + self._shutdown = True + + if self._cleanup_thread and self._cleanup_thread.is_alive(): + self._cleanup_thread.join(timeout=5) + + with self._lock: + # Close all objects + while not self._available.empty(): + try: + obj = self._available.get_nowait() + if hasattr(obj, 'close'): + obj.close() + except queue.Empty: + break + + for obj in list(self._in_use): + if hasattr(obj, 'close'): + obj.close() + + self._in_use.clear() + + print(f"🔚 Pool shutdown completed. Final statistics:\n{self.get_statistics()}") + +class HTTPService: + """Service class that uses the HTTP connection pool""" + + def __init__(self, connection_pool: ObjectPool): + self.connection_pool = connection_pool + + def make_request(self, endpoint: str, method: str = 'GET', **kwargs): + """Make an HTTP request using a pooled connection""" + connection = None + try: + connection = self.connection_pool.acquire(timeout=10) + + if method.upper() == 'GET': + return connection.get(endpoint, **kwargs) + elif method.upper() == 'POST': + return connection.post(endpoint, **kwargs) + elif method.upper() == 'PUT': + return connection.put(endpoint, **kwargs) + elif method.upper() == 'DELETE': + return connection.delete(endpoint, **kwargs) + else: + raise ValueError(f"Unsupported HTTP method: {method}") + + except Exception as e: + print(f"❌ HTTP request failed: {e}") + raise + finally: + if connection: + self.connection_pool.release(connection) + + def batch_requests(self, requests_data: List[Dict[str, Any]]): + """Perform multiple HTTP requests""" + results = [] + + for request_data in requests_data: + try: + endpoint = request_data.get('endpoint', '/') + method = request_data.get('method', 'GET') + params = request_data.get('params', {}) + + result = self.make_request(endpoint, method, **params) + results.append({'success': True, 'response': result}) + + except Exception as e: + results.append({'success': False, 'error': str(e)}) + + return results + +# Worker function for testing concurrent access +def http_worker(worker_id: int, http_service: HTTPService, request_count: int): + """Worker function for concurrent HTTP requests""" + print(f"🏃 Worker {worker_id} started") + + requests_data = [ + {'endpoint': f'users/{i}', 'method': 'GET'}, + {'endpoint': 'posts', 'method': 'POST', 'params': {'json': {'title': f'Post {i} from Worker {worker_id}'}}}, + {'endpoint': f'data/{i}', 'method': 'GET'} + ] + + for i in range(request_count): + try: + request_data = requests_data[i % len(requests_data)] + + # Mock the actual request (since we don't have a real server) + print(f"🔄 Worker {worker_id} making request {i+1}: {request_data['method']} {request_data['endpoint']}") + + # Simulate using the HTTP service + connection = http_service.connection_pool.acquire() + + # Simulate request processing time + time.sleep(0.1) + + print(f"✅ Worker {worker_id} completed request {i+1}") + http_service.connection_pool.release(connection) + + except Exception as e: + print(f"❌ Worker {worker_id} request {i+1} failed: {e}") + + print(f"✅ Worker {worker_id} completed all {request_count} requests") + +def main(): + print("=== Object Pool Pattern Demo - HTTP Connection Pool ===\n") + + # Create HTTP connection pool + def connection_factory(): + connection_id = f"http_conn_{int(time.time() * 1000)}_{threading.current_thread().ident}" + return HTTPConnection(connection_id, "https://jsonplaceholder.typicode.com") + + # Note: Since we don't have a real HTTP server, we'll modify the example + # to simulate the behavior without making actual HTTP requests + + pool = ObjectPool(connection_factory, min_size=2, max_size=6, cleanup_interval=10) + + print("\n1. Basic Pool Operations:") + + try: + # Test basic acquire and release + conn1 = pool.acquire() + conn2 = pool.acquire() + + print(f"Acquired connections:") + print(f" Connection 1: {conn1.get_status()}") + print(f" Connection 2: {conn2.get_status()}") + + print(f"\nPool statistics after acquisition:") + print(pool.get_statistics()) + + # Simulate using connections (without actual HTTP requests) + print(f"✅ Simulated request 1 on {conn1.get_id()}") + conn1.request_count += 1 + conn1.last_used_at = datetime.now() + + print(f"✅ Simulated request 2 on {conn2.get_id()}") + conn2.request_count += 1 + conn2.last_used_at = datetime.now() + + # Return connections to pool + pool.release(conn1) + pool.release(conn2) + + print(f"\nPool statistics after release:") + print(pool.get_statistics()) + + except Exception as e: + print(f"Error in basic operations: {e}") + + print("\n2. HTTP Service Usage:") + + # Create HTTP service + http_service = HTTPService(pool) + + # Simulate service usage + for i in range(5): + try: + connection = pool.acquire() + print(f"🌐 Simulated API call {i+1} using {connection.get_id()}") + + # Simulate request processing + connection.request_count += 1 + connection.last_used_at = datetime.now() + time.sleep(0.05) # Simulate processing time + + pool.release(connection) + + except Exception as e: + print(f"❌ Simulated request {i+1} failed: {e}") + + print(f"\nPool statistics after service usage:") + print(pool.get_statistics()) + + print("\n3. Concurrent Access Testing:") + + # Test with multiple threads + worker_count = 4 + requests_per_worker = 3 + + start_time = time.time() + + with ThreadPoolExecutor(max_workers=worker_count) as executor: + futures = [] + + for i in range(worker_count): + future = executor.submit(http_worker, i+1, http_service, requests_per_worker) + futures.append(future) + + # Wait for all workers to complete + for future in as_completed(futures): + try: + future.result() + except Exception as e: + print(f"Worker failed: {e}") + + end_time = time.time() + + print(f"\n⏱️ Concurrent operations completed in {end_time - start_time:.2f}s") + + print("\n4. Pool Statistics After Concurrent Testing:") + stats = pool.get_statistics() + print(stats) + + print("\n5. Pool Efficiency Analysis:") + print(f"Pool Utilization: {stats.utilization_percentage:.1f}%") + print(f"Object Reuse Rate: {stats.reuse_percentage:.1f}%") + print(f"Average Requests per Object: {stats.total_acquired / stats.total_created:.1f}") + + print("\n6. Testing Pool Exhaustion:") + + # Acquire all connections and try to get one more + held_connections = [] + try: + # Acquire up to max pool size + for i in range(6): # max_size = 6 + conn = pool.acquire(timeout=2) + held_connections.append(conn) + print(f"Acquired connection {i+1}/6: {conn.get_id()}") + + print("Pool is now exhausted:") + print(pool.get_statistics()) + + # Try to acquire one more (should timeout) + try: + extra_conn = pool.acquire(timeout=2) + print("ERROR: Should not have acquired extra connection!") + pool.release(extra_conn) + except TimeoutError as e: + print(f"✅ Expected timeout: {e}") + + except Exception as e: + print(f"Error during exhaustion test: {e}") + + finally: + # Release all held connections + for conn in held_connections: + pool.release(conn) + + print("\n7. Pool After Releasing All Connections:") + print(pool.get_statistics()) + + print("\n8. Testing Object Expiration:") + + # Acquire a connection and simulate aging + test_conn = pool.acquire() + original_max_age = test_conn.max_age + + # Temporarily reduce max age for testing + test_conn.max_age = timedelta(seconds=1) + test_conn.created_at = datetime.now() - timedelta(seconds=2) # Make it old + + print(f"Connection {test_conn.get_id()} is valid: {test_conn.is_valid()}") + + pool.release(test_conn) + + # Wait for cleanup + print("Waiting for automatic cleanup...") + time.sleep(2) + + print("Pool statistics after expiration cleanup:") + print(pool.get_statistics()) + + # Shutdown pool + print("\n9. Pool Shutdown:") + pool.shutdown() + + print("\n✅ Object Pool pattern successfully demonstrated!") + print("Benefits: Resource reuse, performance optimization, controlled allocation, automatic cleanup") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/observer/solution.cpp b/src/content/design-pattern/code/observer/solution.cpp new file mode 100644 index 0000000..f1714e6 --- /dev/null +++ b/src/content/design-pattern/code/observer/solution.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include + +// Observer interface +class Observer { +public: + virtual ~Observer() = default; + virtual void update(const std::string& news) = 0; +}; + +// Subject interface +class Subject { +public: + virtual ~Subject() = default; + virtual void subscribe(std::shared_ptr observer) = 0; + virtual void unsubscribe(std::shared_ptr observer) = 0; + virtual void notifyObservers() = 0; +}; + +// Concrete Subject - News Agency +class NewsAgency : public Subject { +private: + std::vector> observers; + std::string latestNews; + +public: + void subscribe(std::shared_ptr observer) override { + observers.push_back(observer); + std::cout << "Observer subscribed. Total subscribers: " << observers.size() << std::endl; + } + + void unsubscribe(std::shared_ptr observer) override { + auto it = std::find(observers.begin(), observers.end(), observer); + if (it != observers.end()) { + observers.erase(it); + std::cout << "Observer unsubscribed. Total subscribers: " << observers.size() << std::endl; + } + } + + void notifyObservers() override { + std::cout << "\n--- BROADCASTING NEWS ---" << std::endl; + for (const auto& observer : observers) { + if (auto obs = observer.lock ? observer : observer) { // Handle weak_ptr if needed + obs->update(latestNews); + } + } + std::cout << "------------------------\n" << std::endl; + } + + void setNews(const std::string& news) { + this->latestNews = news; + std::cout << "NEWS ALERT: " << news << std::endl; + notifyObservers(); + } + + std::string getLatestNews() const { + return latestNews; + } +}; + +// Concrete Observers +class NewsChannel : public Observer { +private: + std::string channelName; + +public: + NewsChannel(const std::string& channelName) : channelName(channelName) {} + + void update(const std::string& news) override { + std::cout << "[" << channelName << " TV] Broadcasting: " << news << std::endl; + } +}; + +class Newspaper : public Observer { +private: + std::string paperName; + +public: + Newspaper(const std::string& paperName) : paperName(paperName) {} + + void update(const std::string& news) override { + std::cout << "[" << paperName << " Newspaper] Publishing: " << news << std::endl; + } +}; + +class OnlineNews : public Observer { +private: + std::string websiteName; + +public: + OnlineNews(const std::string& websiteName) : websiteName(websiteName) {} + + void update(const std::string& news) override { + std::cout << "[" << websiteName << " Website] Posted: " << news << std::endl; + } +}; + +class MobileApp : public Observer { +private: + std::string appName; + std::string userEmail; + +public: + MobileApp(const std::string& appName, const std::string& userEmail) + : appName(appName), userEmail(userEmail) {} + + void update(const std::string& news) override { + std::cout << "[" << appName << " App] Push notification to " << userEmail << ": " << news << std::endl; + } +}; + +int main() { + // Create the subject (news agency) + auto newsAgency = std::make_unique(); + + // Create observers + auto cnn = std::make_shared("CNN"); + auto bbc = std::make_shared("BBC"); + auto nytimes = std::make_shared("NY Times"); + auto techcrunch = std::make_shared("TechCrunch"); + auto newsApp = std::make_shared("NewsBreaker", "user@example.com"); + + std::cout << "=== SUBSCRIPTION PHASE ===" << std::endl; + // Subscribe observers + newsAgency->subscribe(cnn); + newsAgency->subscribe(bbc); + newsAgency->subscribe(nytimes); + newsAgency->subscribe(techcrunch); + newsAgency->subscribe(newsApp); + + std::cout << "\n=== NEWS UPDATES ===" << std::endl; + // Publish news updates + newsAgency->setNews("Breaking: New AI breakthrough announced!"); + + newsAgency->setNews("Tech giants report record quarterly earnings"); + + std::cout << "\n=== UNSUBSCRIPTION ===" << std::endl; + // Unsubscribe some observers + newsAgency->unsubscribe(bbc); + newsAgency->unsubscribe(nytimes); + + // Publish another update + newsAgency->setNews("Climate summit reaches historic agreement"); + + std::cout << "\n=== RE-SUBSCRIPTION ===" << std::endl; + // Re-subscribe + newsAgency->subscribe(bbc); + + newsAgency->setNews("Sports: World Cup final ends in dramatic victory!"); + + return 0; +} diff --git a/src/content/design-pattern/code/observer/solution.java b/src/content/design-pattern/code/observer/solution.java new file mode 100644 index 0000000..6957935 --- /dev/null +++ b/src/content/design-pattern/code/observer/solution.java @@ -0,0 +1,151 @@ +import java.util.*; + +// Observer interface +interface Observer { + void update(String news); +} + +// Subject interface +interface Subject { + void subscribe(Observer observer); + void unsubscribe(Observer observer); + void notifyObservers(); +} + +// Concrete Subject - News Agency +class NewsAgency implements Subject { + private List observers; + private String latestNews; + + public NewsAgency() { + this.observers = new ArrayList<>(); + } + + @Override + public void subscribe(Observer observer) { + observers.add(observer); + System.out.println("Observer subscribed. Total subscribers: " + observers.size()); + } + + @Override + public void unsubscribe(Observer observer) { + observers.remove(observer); + System.out.println("Observer unsubscribed. Total subscribers: " + observers.size()); + } + + @Override + public void notifyObservers() { + System.out.println("\n--- BROADCASTING NEWS ---"); + for (Observer observer : observers) { + observer.update(latestNews); + } + System.out.println("------------------------\n"); + } + + public void setNews(String news) { + this.latestNews = news; + System.out.println("NEWS ALERT: " + news); + notifyObservers(); + } + + public String getLatestNews() { + return latestNews; + } +} + +// Concrete Observers +class NewsChannel implements Observer { + private String channelName; + + public NewsChannel(String channelName) { + this.channelName = channelName; + } + + @Override + public void update(String news) { + System.out.println("[" + channelName + " TV] Broadcasting: " + news); + } +} + +class Newspaper implements Observer { + private String paperName; + + public Newspaper(String paperName) { + this.paperName = paperName; + } + + @Override + public void update(String news) { + System.out.println("[" + paperName + " Newspaper] Publishing: " + news); + } +} + +class OnlineNews implements Observer { + private String websiteName; + + public OnlineNews(String websiteName) { + this.websiteName = websiteName; + } + + @Override + public void update(String news) { + System.out.println("[" + websiteName + " Website] Posted: " + news); + } +} + +class MobileApp implements Observer { + private String appName; + private String userEmail; + + public MobileApp(String appName, String userEmail) { + this.appName = appName; + this.userEmail = userEmail; + } + + @Override + public void update(String news) { + System.out.println("[" + appName + " App] Push notification to " + userEmail + ": " + news); + } +} + +public class ObserverPatternDemo { + public static void main(String[] args) { + // Create the subject (news agency) + NewsAgency newsAgency = new NewsAgency(); + + // Create observers + NewsChannel cnn = new NewsChannel("CNN"); + NewsChannel bbc = new NewsChannel("BBC"); + Newspaper nytimes = new Newspaper("NY Times"); + OnlineNews techcrunch = new OnlineNews("TechCrunch"); + MobileApp newsApp = new MobileApp("NewsBreaker", "user@example.com"); + + System.out.println("=== SUBSCRIPTION PHASE ==="); + // Subscribe observers + newsAgency.subscribe(cnn); + newsAgency.subscribe(bbc); + newsAgency.subscribe(nytimes); + newsAgency.subscribe(techcrunch); + newsAgency.subscribe(newsApp); + + System.out.println("\n=== NEWS UPDATES ==="); + // Publish news updates + newsAgency.setNews("Breaking: New AI breakthrough announced!"); + + newsAgency.setNews("Tech giants report record quarterly earnings"); + + System.out.println("\n=== UNSUBSCRIPTION ==="); + // Unsubscribe some observers + newsAgency.unsubscribe(bbc); + newsAgency.unsubscribe(nytimes); + + // Publish another update + newsAgency.setNews("Climate summit reaches historic agreement"); + + System.out.println("\n=== RE-SUBSCRIPTION ==="); + // Re-subscribe + newsAgency.subscribe(bbc); + + newsAgency.setNews("Sports: World Cup final ends in dramatic victory!"); + } +} diff --git a/src/content/design-pattern/code/observer/solution.py b/src/content/design-pattern/code/observer/solution.py new file mode 100644 index 0000000..3f29c2d --- /dev/null +++ b/src/content/design-pattern/code/observer/solution.py @@ -0,0 +1,123 @@ +from abc import ABC, abstractmethod +from typing import List + +# Observer interface +class Observer(ABC): + @abstractmethod + def update(self, news: str) -> None: + pass + +# Subject interface +class Subject(ABC): + @abstractmethod + def subscribe(self, observer: Observer) -> None: + pass + + @abstractmethod + def unsubscribe(self, observer: Observer) -> None: + pass + + @abstractmethod + def notify_observers(self) -> None: + pass + +# Concrete Subject - News Agency +class NewsAgency(Subject): + def __init__(self): + self._observers: List[Observer] = [] + self._latest_news: str = "" + + def subscribe(self, observer: Observer) -> None: + self._observers.append(observer) + print(f"Observer subscribed. Total subscribers: {len(self._observers)}") + + def unsubscribe(self, observer: Observer) -> None: + if observer in self._observers: + self._observers.remove(observer) + print(f"Observer unsubscribed. Total subscribers: {len(self._observers)}") + + def notify_observers(self) -> None: + print("\n--- BROADCASTING NEWS ---") + for observer in self._observers: + observer.update(self._latest_news) + print("------------------------\n") + + def set_news(self, news: str) -> None: + self._latest_news = news + print(f"NEWS ALERT: {news}") + self.notify_observers() + + def get_latest_news(self) -> str: + return self._latest_news + +# Concrete Observers +class NewsChannel(Observer): + def __init__(self, channel_name: str): + self._channel_name = channel_name + + def update(self, news: str) -> None: + print(f"[{self._channel_name} TV] Broadcasting: {news}") + +class Newspaper(Observer): + def __init__(self, paper_name: str): + self._paper_name = paper_name + + def update(self, news: str) -> None: + print(f"[{self._paper_name} Newspaper] Publishing: {news}") + +class OnlineNews(Observer): + def __init__(self, website_name: str): + self._website_name = website_name + + def update(self, news: str) -> None: + print(f"[{self._website_name} Website] Posted: {news}") + +class MobileApp(Observer): + def __init__(self, app_name: str, user_email: str): + self._app_name = app_name + self._user_email = user_email + + def update(self, news: str) -> None: + print(f"[{self._app_name} App] Push notification to {self._user_email}: {news}") + +def main(): + # Create the subject (news agency) + news_agency = NewsAgency() + + # Create observers + cnn = NewsChannel("CNN") + bbc = NewsChannel("BBC") + nytimes = Newspaper("NY Times") + techcrunch = OnlineNews("TechCrunch") + news_app = MobileApp("NewsBreaker", "user@example.com") + + print("=== SUBSCRIPTION PHASE ===") + # Subscribe observers + news_agency.subscribe(cnn) + news_agency.subscribe(bbc) + news_agency.subscribe(nytimes) + news_agency.subscribe(techcrunch) + news_agency.subscribe(news_app) + + print("\n=== NEWS UPDATES ===") + # Publish news updates + news_agency.set_news("Breaking: New AI breakthrough announced!") + + news_agency.set_news("Tech giants report record quarterly earnings") + + print("\n=== UNSUBSCRIPTION ===") + # Unsubscribe some observers + news_agency.unsubscribe(bbc) + news_agency.unsubscribe(nytimes) + + # Publish another update + news_agency.set_news("Climate summit reaches historic agreement") + + print("\n=== RE-SUBSCRIPTION ===") + # Re-subscribe + news_agency.subscribe(bbc) + + news_agency.set_news("Sports: World Cup final ends in dramatic victory!") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/private-class-data/solution.cpp b/src/content/design-pattern/code/private-class-data/solution.cpp new file mode 100644 index 0000000..bab25af --- /dev/null +++ b/src/content/design-pattern/code/private-class-data/solution.cpp @@ -0,0 +1,444 @@ +/** + * Private Class Data Pattern - C++ Implementation + * + * This pattern encapsulates class data into a separate private data class + * to prevent unwanted modification and provide controlled access. + * + * Example: BankAccount with sensitive financial data protection + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +// BEFORE: Traditional approach - direct member access (vulnerable) +class VulnerableBankAccount { +private: + std::string accountNumber; + std::string ownerName; + double balance; + std::string pin; + +public: + VulnerableBankAccount(const std::string& accountNumber, const std::string& ownerName, + double balance, const std::string& pin) + : accountNumber(accountNumber), ownerName(ownerName), balance(balance), pin(pin) {} + + // Problem: Direct member access can accidentally modify sensitive data + bool validateTransaction(double amount) { + if (this->balance >= amount) { + this->balance -= amount; // Oops! This should only happen in actual transaction + return true; + } + return false; + } + + // Direct access to internal state + double getBalance() const { return balance; } + std::string getAccountNumber() const { return accountNumber; } +}; + +// AFTER: Private Class Data Pattern Implementation + +// Step 1: Create immutable private data class +class AccountData { +private: + const std::string accountNumber; + const std::string ownerName; + const double balance; + const std::string pin; + const std::chrono::system_clock::time_point lastTransactionTime; + +public: + // Constructor + AccountData(const std::string& accountNumber, const std::string& ownerName, + double balance, const std::string& pin) + : accountNumber(accountNumber), ownerName(ownerName), balance(balance), + pin(pin), lastTransactionTime(std::chrono::system_clock::now()) {} + + // Copy constructor for creating modified instances + AccountData(const std::string& accountNumber, const std::string& ownerName, + double balance, const std::string& pin, + const std::chrono::system_clock::time_point& lastTransactionTime) + : accountNumber(accountNumber), ownerName(ownerName), balance(balance), + pin(pin), lastTransactionTime(lastTransactionTime) {} + + // Controlled access methods + std::string getAccountNumber() const { return accountNumber; } + std::string getOwnerName() const { return ownerName; } + double getBalance() const { return balance; } + std::chrono::system_clock::time_point getLastTransactionTime() const { return lastTransactionTime; } + + // Secure PIN validation without exposing the PIN + bool validatePin(const std::string& inputPin) const { + return !pin.empty() && pin == inputPin; + } + + // Create new instance with updated balance (immutability) + std::unique_ptr withNewBalance(double newBalance) const { + return std::make_unique(accountNumber, ownerName, newBalance, pin, + std::chrono::system_clock::now()); + } + + // Create masked account number for display + std::string getMaskedAccountNumber() const { + if (accountNumber.length() <= 4) return "****"; + return "****" + accountNumber.substr(accountNumber.length() - 4); + } + + // Get account summary without sensitive data + std::string getAccountSummary() const { + std::ostringstream oss; + auto time_t = std::chrono::system_clock::to_time_t(lastTransactionTime); + oss << "Account: " << getMaskedAccountNumber() + << ", Owner: " << ownerName + << ", Balance: $" << std::fixed << std::setprecision(2) << balance + << ", Last Transaction: " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S"); + return oss.str(); + } +}; + +// Step 2: Main class uses private data object +class SecureBankAccount { +private: + std::unique_ptr accountData; + +public: + SecureBankAccount(const std::string& accountNumber, const std::string& ownerName, + double initialBalance, const std::string& pin) + : accountData(std::make_unique(accountNumber, ownerName, initialBalance, pin)) {} + + // Copy constructor + SecureBankAccount(const SecureBankAccount& other) + : accountData(std::make_unique(*other.accountData)) {} + + // Move constructor + SecureBankAccount(SecureBankAccount&& other) noexcept + : accountData(std::move(other.accountData)) {} + + // Assignment operators + SecureBankAccount& operator=(const SecureBankAccount& other) { + if (this != &other) { + accountData = std::make_unique(*other.accountData); + } + return *this; + } + + SecureBankAccount& operator=(SecureBankAccount&& other) noexcept { + if (this != &other) { + accountData = std::move(other.accountData); + } + return *this; + } + + // Secure transaction with PIN validation + bool withdraw(double amount, const std::string& pin) { + if (!accountData->validatePin(pin)) { + std::cout << "Invalid PIN" << std::endl; + return false; + } + + if (accountData->getBalance() < amount) { + std::cout << "Insufficient funds" << std::endl; + return false; + } + + if (amount <= 0) { + std::cout << "Invalid amount" << std::endl; + return false; + } + + // Create new account data with updated balance (immutable update) + accountData = accountData->withNewBalance(accountData->getBalance() - amount); + std::cout << "Withdrawal successful. New balance: $" + << std::fixed << std::setprecision(2) << accountData->getBalance() << std::endl; + return true; + } + + bool deposit(double amount, const std::string& pin) { + if (!accountData->validatePin(pin)) { + std::cout << "Invalid PIN" << std::endl; + return false; + } + + if (amount <= 0) { + std::cout << "Invalid amount" << std::endl; + return false; + } + + accountData = accountData->withNewBalance(accountData->getBalance() + amount); + std::cout << "Deposit successful. New balance: $" + << std::fixed << std::setprecision(2) << accountData->getBalance() << std::endl; + return true; + } + + // Safe validation that doesn't modify state + bool validateTransaction(double amount) const { + return accountData->getBalance() >= amount; + } + + // Controlled access to account information + std::string getAccountSummary() const { + return accountData->getAccountSummary(); + } + + std::string getMaskedAccountNumber() const { + return accountData->getMaskedAccountNumber(); + } + + std::string getOwnerName() const { + return accountData->getOwnerName(); + } + + double getBalance(const std::string& pin) const { + if (accountData->validatePin(pin)) { + return accountData->getBalance(); + } + throw std::runtime_error("Invalid PIN for balance inquiry"); + } + + // Transfer functionality + bool transferTo(SecureBankAccount& recipient, double amount, const std::string& pin) { + if (!accountData->validatePin(pin)) { + std::cout << "Invalid PIN for transfer" << std::endl; + return false; + } + + if (!validateTransaction(amount)) { + std::cout << "Insufficient funds for transfer" << std::endl; + return false; + } + + // Perform withdrawal + accountData = accountData->withNewBalance(accountData->getBalance() - amount); + + // Perform deposit to recipient (simplified - in real system would need recipient's PIN) + recipient.accountData = recipient.accountData->withNewBalance( + recipient.accountData->getBalance() + amount); + + std::cout << "Transfer of $" << std::fixed << std::setprecision(2) + << amount << " completed successfully" << std::endl; + return true; + } +}; + +// Alternative implementation using PIMPL idiom (Pointer to Implementation) +class PimplBankAccount { +private: + class Impl; // Forward declaration + std::unique_ptr pImpl; // Pointer to implementation + +public: + // Constructor + PimplBankAccount(const std::string& accountNumber, const std::string& ownerName, + double initialBalance, const std::string& pin); + + // Destructor + ~PimplBankAccount(); + + // Copy and move operations + PimplBankAccount(const PimplBankAccount& other); + PimplBankAccount(PimplBankAccount&& other) noexcept; + PimplBankAccount& operator=(const PimplBankAccount& other); + PimplBankAccount& operator=(PimplBankAccount&& other) noexcept; + + // Public interface + bool withdraw(double amount, const std::string& pin); + bool deposit(double amount, const std::string& pin); + bool validateTransaction(double amount) const; + std::string getAccountSummary() const; + std::string getMaskedAccountNumber() const; + double getBalance(const std::string& pin) const; +}; + +// Implementation class (would typically be in .cpp file) +class PimplBankAccount::Impl { +public: + std::unique_ptr accountData; + + Impl(const std::string& accountNumber, const std::string& ownerName, + double initialBalance, const std::string& pin) + : accountData(std::make_unique(accountNumber, ownerName, initialBalance, pin)) {} +}; + +// PimplBankAccount method implementations +PimplBankAccount::PimplBankAccount(const std::string& accountNumber, const std::string& ownerName, + double initialBalance, const std::string& pin) + : pImpl(std::make_unique(accountNumber, ownerName, initialBalance, pin)) {} + +PimplBankAccount::~PimplBankAccount() = default; + +PimplBankAccount::PimplBankAccount(const PimplBankAccount& other) + : pImpl(std::make_unique(*other.pImpl)) {} + +PimplBankAccount::PimplBankAccount(PimplBankAccount&& other) noexcept = default; + +PimplBankAccount& PimplBankAccount::operator=(const PimplBankAccount& other) { + if (this != &other) { + pImpl = std::make_unique(*other.pImpl); + } + return *this; +} + +PimplBankAccount& PimplBankAccount::operator=(PimplBankAccount&& other) noexcept = default; + +bool PimplBankAccount::withdraw(double amount, const std::string& pin) { + if (!pImpl->accountData->validatePin(pin)) { + std::cout << "Invalid PIN" << std::endl; + return false; + } + + if (pImpl->accountData->getBalance() < amount || amount <= 0) { + std::cout << "Invalid transaction" << std::endl; + return false; + } + + pImpl->accountData = pImpl->accountData->withNewBalance(pImpl->accountData->getBalance() - amount); + std::cout << "PIMPL Withdrawal successful. New balance: $" + << std::fixed << std::setprecision(2) << pImpl->accountData->getBalance() << std::endl; + return true; +} + +bool PimplBankAccount::deposit(double amount, const std::string& pin) { + if (!pImpl->accountData->validatePin(pin) || amount <= 0) { + std::cout << "Invalid transaction" << std::endl; + return false; + } + + pImpl->accountData = pImpl->accountData->withNewBalance(pImpl->accountData->getBalance() + amount); + return true; +} + +bool PimplBankAccount::validateTransaction(double amount) const { + return pImpl->accountData->getBalance() >= amount; +} + +std::string PimplBankAccount::getAccountSummary() const { + return pImpl->accountData->getAccountSummary(); +} + +std::string PimplBankAccount::getMaskedAccountNumber() const { + return pImpl->accountData->getMaskedAccountNumber(); +} + +double PimplBankAccount::getBalance(const std::string& pin) const { + if (pImpl->accountData->validatePin(pin)) { + return pImpl->accountData->getBalance(); + } + throw std::runtime_error("Invalid PIN for balance inquiry"); +} + +// Demonstration function +void demonstratePrivateClassDataPattern() { + std::cout << "=== Private Class Data Pattern Demo ===" << std::endl << std::endl; + + // Create secure bank account + SecureBankAccount account("1234567890", "Charlie Brown", 2000.0, "5678"); + + // Display initial account summary + std::cout << "Initial Account Summary:" << std::endl; + std::cout << account.getAccountSummary() << std::endl << std::endl; + + // Valid transactions + std::cout << "--- Valid Transactions ---" << std::endl; + account.withdraw(300.0, "5678"); + account.deposit(150.0, "5678"); + std::cout << std::endl; + + // Invalid transactions + std::cout << "--- Invalid Transactions ---" << std::endl; + account.withdraw(500.0, "0000"); // Wrong PIN + account.withdraw(3000.0, "5678"); // Insufficient funds + std::cout << std::endl; + + // Safe validation + std::cout << "--- Transaction Validation ---" << std::endl; + std::cout << "Can withdraw $800? " << (account.validateTransaction(800.0) ? "Yes" : "No") << std::endl; + std::cout << "Can withdraw $2500? " << (account.validateTransaction(2500.0) ? "Yes" : "No") << std::endl; + std::cout << std::endl; + + // Security demonstration + std::cout << "--- Security Features ---" << std::endl; + std::cout << "Masked Account Number: " << account.getMaskedAccountNumber() << std::endl; + std::cout << "Owner Name: " << account.getOwnerName() << std::endl; + + try { + double balance = account.getBalance("5678"); + std::cout << "Balance with correct PIN: $" << std::fixed << std::setprecision(2) << balance << std::endl; + } catch (const std::runtime_error& e) { + std::cout << "Security error: " << e.what() << std::endl; + } + + try { + account.getBalance("0000"); // Wrong PIN + } catch (const std::runtime_error& e) { + std::cout << "Security error with wrong PIN: " << e.what() << std::endl; + } + + std::cout << std::endl; + + // Transfer demonstration + std::cout << "--- Transfer Operation ---" << std::endl; + SecureBankAccount recipient("0987654321", "Diana Prince", 500.0, "9999"); + std::cout << "Before transfer:" << std::endl; + std::cout << "Sender: " << account.getAccountSummary() << std::endl; + std::cout << "Recipient: " << recipient.getAccountSummary() << std::endl; + + account.transferTo(recipient, 200.0, "5678"); + + std::cout << "After transfer:" << std::endl; + std::cout << "Sender: " << account.getAccountSummary() << std::endl; + std::cout << "Recipient: " << recipient.getAccountSummary() << std::endl; + std::cout << std::endl; + + // PIMPL demonstration + std::cout << "--- PIMPL Implementation ---" << std::endl; + PimplBankAccount pimplAccount("1111222233", "Eve Wilson", 1000.0, "1122"); + std::cout << "PIMPL Account: " << pimplAccount.getAccountSummary() << std::endl; + pimplAccount.withdraw(100.0, "1122"); + std::cout << "Final PIMPL Account: " << pimplAccount.getAccountSummary() << std::endl; +} + +int main() { + demonstratePrivateClassDataPattern(); + return 0; +} + +/* +Benefits of Private Class Data Pattern in C++: + +1. ENCAPSULATION: Sensitive data is completely encapsulated in private data class +2. IMMUTABILITY: Data objects can be made const and immutable +3. CONTROLLED ACCESS: All data access goes through validated methods +4. SECURITY: No direct access to sensitive information like PINs +5. CONSISTENCY: State changes are atomic and consistent +6. MEMORY SAFETY: Uses smart pointers for automatic memory management +7. CONST CORRECTNESS: Proper const methods for read-only operations +8. PIMPL COMPATIBILITY: Works well with Pointer to Implementation idiom + +C++-specific features used: +- const members for immutability +- std::unique_ptr for automatic memory management +- Move semantics for efficient transfers +- RAII for resource management +- const correctness throughout +- Exception handling for security validation +- Copy and move constructors/assignment operators + +Use Cases: +- Financial systems with sensitive account data +- User profiles with personal information +- Configuration classes with system settings +- Game state management with protected player data +- Any class where data integrity and security is critical + +Comparison with PIMPL: +- Private Class Data: Focuses on data encapsulation and immutability +- PIMPL: Focuses on compilation firewall and interface stability +- Both can be combined for maximum benefit +*/ diff --git a/src/content/design-pattern/code/private-class-data/solution.java b/src/content/design-pattern/code/private-class-data/solution.java new file mode 100644 index 0000000..b5f1133 --- /dev/null +++ b/src/content/design-pattern/code/private-class-data/solution.java @@ -0,0 +1,228 @@ +/** + * Private Class Data Pattern - Java Implementation + * + * This pattern encapsulates class data into a separate private data class + * to prevent unwanted modification and provide controlled access. + * + * Example: BankAccount with sensitive financial data protection + */ + +// BEFORE: Traditional approach - direct field access (vulnerable) +class VulnerableBankAccount { + private String accountNumber; + private String ownerName; + private double balance; + private String pin; + + public VulnerableBankAccount(String accountNumber, String ownerName, double balance, String pin) { + this.accountNumber = accountNumber; + this.ownerName = ownerName; + this.balance = balance; + this.pin = pin; + } + + // Problem: Direct field access in methods can accidentally modify sensitive data + public boolean validateTransaction(double amount) { + // Accidentally modifying balance during validation + if (this.balance >= amount) { + this.balance -= amount; // Oops! This should only happen in actual transaction + return true; + } + return false; + } + + // Getters expose internal state directly + public String getAccountNumber() { return accountNumber; } + public double getBalance() { return balance; } +} + +// AFTER: Private Class Data Pattern Implementation + +// Step 1: Create immutable private data class +final class AccountData { + private final String accountNumber; + private final String ownerName; + private final double balance; + private final String pin; + private final long lastTransactionTime; + + public AccountData(String accountNumber, String ownerName, double balance, String pin) { + this.accountNumber = accountNumber; + this.ownerName = ownerName; + this.balance = balance; + this.pin = pin; + this.lastTransactionTime = System.currentTimeMillis(); + } + + // Private constructor for creating modified copies + private AccountData(String accountNumber, String ownerName, double balance, String pin, long lastTransactionTime) { + this.accountNumber = accountNumber; + this.ownerName = ownerName; + this.balance = balance; + this.pin = pin; + this.lastTransactionTime = lastTransactionTime; + } + + // Controlled access methods + public String getAccountNumber() { return accountNumber; } + public String getOwnerName() { return ownerName; } + public double getBalance() { return balance; } + public long getLastTransactionTime() { return lastTransactionTime; } + + // Secure PIN validation without exposing the PIN + public boolean validatePin(String inputPin) { + return this.pin != null && this.pin.equals(inputPin); + } + + // Create new instance with updated balance (immutability) + public AccountData withNewBalance(double newBalance) { + return new AccountData(accountNumber, ownerName, newBalance, pin, System.currentTimeMillis()); + } + + // Create masked account number for display + public String getMaskedAccountNumber() { + if (accountNumber.length() <= 4) return "****"; + return "****" + accountNumber.substring(accountNumber.length() - 4); + } +} + +// Step 2: Main class uses private data object +class SecureBankAccount { + private AccountData accountData; + + public SecureBankAccount(String accountNumber, String ownerName, double initialBalance, String pin) { + this.accountData = new AccountData(accountNumber, ownerName, initialBalance, pin); + } + + // Secure transaction with PIN validation + public boolean withdraw(double amount, String pin) { + if (!accountData.validatePin(pin)) { + System.out.println("Invalid PIN"); + return false; + } + + if (accountData.getBalance() < amount) { + System.out.println("Insufficient funds"); + return false; + } + + // Create new account data with updated balance (immutable update) + this.accountData = accountData.withNewBalance(accountData.getBalance() - amount); + System.out.println("Withdrawal successful. New balance: $" + accountData.getBalance()); + return true; + } + + public boolean deposit(double amount, String pin) { + if (!accountData.validatePin(pin)) { + System.out.println("Invalid PIN"); + return false; + } + + if (amount <= 0) { + System.out.println("Invalid amount"); + return false; + } + + this.accountData = accountData.withNewBalance(accountData.getBalance() + amount); + System.out.println("Deposit successful. New balance: $" + accountData.getBalance()); + return true; + } + + // Safe validation that doesn't modify state + public boolean validateTransaction(double amount) { + return accountData.getBalance() >= amount; + } + + // Controlled access to account information + public String getAccountSummary() { + return String.format("Account: %s, Owner: %s, Balance: $%.2f, Last Transaction: %s", + accountData.getMaskedAccountNumber(), + accountData.getOwnerName(), + accountData.getBalance(), + new java.util.Date(accountData.getLastTransactionTime()) + ); + } + + // No direct access to sensitive data like PIN or full account number + public String getMaskedAccountNumber() { + return accountData.getMaskedAccountNumber(); + } + + public double getBalance(String pin) { + if (accountData.validatePin(pin)) { + return accountData.getBalance(); + } + throw new SecurityException("Invalid PIN for balance inquiry"); + } +} + +// Demonstration class +public class PrivateClassDataDemo { + public static void main(String[] args) { + System.out.println("=== Private Class Data Pattern Demo ===\n"); + + // Create secure bank account + SecureBankAccount account = new SecureBankAccount("1234567890", "John Doe", 1000.0, "1234"); + + // Display account summary + System.out.println("Initial Account Summary:"); + System.out.println(account.getAccountSummary()); + System.out.println(); + + // Valid transactions + System.out.println("--- Valid Transactions ---"); + account.withdraw(100.0, "1234"); + account.deposit(50.0, "1234"); + System.out.println(); + + // Invalid transactions + System.out.println("--- Invalid Transactions ---"); + account.withdraw(200.0, "0000"); // Wrong PIN + account.withdraw(2000.0, "1234"); // Insufficient funds + System.out.println(); + + // Safe validation + System.out.println("--- Transaction Validation ---"); + System.out.println("Can withdraw $500? " + account.validateTransaction(500.0)); + System.out.println("Can withdraw $1000? " + account.validateTransaction(1000.0)); + System.out.println(); + + // Final summary + System.out.println("Final Account Summary:"); + System.out.println(account.getAccountSummary()); + + // Demonstrate security + System.out.println("\n--- Security Features ---"); + System.out.println("Masked Account Number: " + account.getMaskedAccountNumber()); + try { + double balance = account.getBalance("1234"); + System.out.println("Balance with correct PIN: $" + balance); + } catch (SecurityException e) { + System.out.println("Security error: " + e.getMessage()); + } + + try { + account.getBalance("0000"); // Wrong PIN + } catch (SecurityException e) { + System.out.println("Security error with wrong PIN: " + e.getMessage()); + } + } +} + +/* +Benefits of Private Class Data Pattern: + +1. ENCAPSULATION: Sensitive data is completely encapsulated in private data class +2. IMMUTABILITY: Data objects are immutable, preventing accidental modification +3. CONTROLLED ACCESS: All data access goes through controlled methods +4. SECURITY: No direct access to sensitive information like PINs +5. CONSISTENCY: State changes are atomic and consistent +6. THREAD SAFETY: Immutable objects are inherently thread-safe +7. AUDIT TRAIL: Can easily add logging/auditing to data access methods + +Use Cases: +- Financial systems with sensitive account data +- User profiles with personal information +- Configuration classes with system settings +- Any class where data integrity is critical +*/ diff --git a/src/content/design-pattern/code/private-class-data/solution.py b/src/content/design-pattern/code/private-class-data/solution.py new file mode 100644 index 0000000..a430c60 --- /dev/null +++ b/src/content/design-pattern/code/private-class-data/solution.py @@ -0,0 +1,348 @@ +""" +Private Class Data Pattern - Python Implementation + +This pattern encapsulates class data into a separate private data class +to prevent unwanted modification and provide controlled access. + +Example: BankAccount with sensitive financial data protection +""" + +import time +from dataclasses import dataclass, replace +from typing import Optional +import hashlib + +# BEFORE: Traditional approach - direct attribute access (vulnerable) +class VulnerableBankAccount: + def __init__(self, account_number: str, owner_name: str, balance: float, pin: str): + self.account_number = account_number + self.owner_name = owner_name + self.balance = balance + self.pin = pin + + def validate_transaction(self, amount: float) -> bool: + # Problem: Direct attribute access can accidentally modify sensitive data + if self.balance >= amount: + self.balance -= amount # Oops! This should only happen in actual transaction + return True + return False + + # Direct attribute exposure + def get_balance(self) -> float: + return self.balance + +# AFTER: Private Class Data Pattern Implementation + +# Step 1: Create immutable private data class using dataclass +@dataclass(frozen=True) # frozen=True makes it immutable +class _AccountData: + """Private data class for sensitive account information.""" + account_number: str + owner_name: str + balance: float + _pin_hash: str + last_transaction_time: float + + @classmethod + def create(cls, account_number: str, owner_name: str, balance: float, pin: str) -> '_AccountData': + """Factory method to create account data with hashed PIN.""" + pin_hash = hashlib.sha256(pin.encode()).hexdigest() + return cls( + account_number=account_number, + owner_name=owner_name, + balance=balance, + _pin_hash=pin_hash, + last_transaction_time=time.time() + ) + + def validate_pin(self, input_pin: str) -> bool: + """Validate PIN without exposing the stored PIN hash.""" + input_hash = hashlib.sha256(input_pin.encode()).hexdigest() + return self._pin_hash == input_hash + + def with_new_balance(self, new_balance: float) -> '_AccountData': + """Create new instance with updated balance (maintaining immutability).""" + return replace(self, balance=new_balance, last_transaction_time=time.time()) + + def get_masked_account_number(self) -> str: + """Return masked account number for display.""" + if len(self.account_number) <= 4: + return "****" + return "****" + self.account_number[-4:] + + def get_account_summary(self) -> str: + """Get safe account summary without sensitive data.""" + return (f"Account: {self.get_masked_account_number()}, " + f"Owner: {self.owner_name}, " + f"Balance: ${self.balance:.2f}, " + f"Last Transaction: {time.ctime(self.last_transaction_time)}") + +# Step 2: Main class uses private data object +class SecureBankAccount: + def __init__(self, account_number: str, owner_name: str, initial_balance: float, pin: str): + self._account_data = _AccountData.create(account_number, owner_name, initial_balance, pin) + + def withdraw(self, amount: float, pin: str) -> bool: + """Withdraw money with PIN validation.""" + if not self._account_data.validate_pin(pin): + print("Invalid PIN") + return False + + if self._account_data.balance < amount: + print("Insufficient funds") + return False + + if amount <= 0: + print("Invalid amount") + return False + + # Create new account data with updated balance (immutable update) + new_balance = self._account_data.balance - amount + self._account_data = self._account_data.with_new_balance(new_balance) + print(f"Withdrawal successful. New balance: ${self._account_data.balance:.2f}") + return True + + def deposit(self, amount: float, pin: str) -> bool: + """Deposit money with PIN validation.""" + if not self._account_data.validate_pin(pin): + print("Invalid PIN") + return False + + if amount <= 0: + print("Invalid amount") + return False + + new_balance = self._account_data.balance + amount + self._account_data = self._account_data.with_new_balance(new_balance) + print(f"Deposit successful. New balance: ${self._account_data.balance:.2f}") + return True + + def validate_transaction(self, amount: float) -> bool: + """Safe validation that doesn't modify state.""" + return self._account_data.balance >= amount + + def get_account_summary(self) -> str: + """Get account summary with controlled access.""" + return self._account_data.get_account_summary() + + def get_masked_account_number(self) -> str: + """Get masked account number for display.""" + return self._account_data.get_masked_account_number() + + def get_balance(self, pin: str) -> float: + """Get balance with PIN validation.""" + if self._account_data.validate_pin(pin): + return self._account_data.balance + raise SecurityError("Invalid PIN for balance inquiry") + + def get_owner_name(self) -> str: + """Get owner name (non-sensitive data).""" + return self._account_data.owner_name + +# Alternative implementation using properties and private attributes +class AlternativeSecureAccount: + """Alternative implementation using Python's property decorators.""" + + def __init__(self, account_number: str, owner_name: str, initial_balance: float, pin: str): + # Use name mangling for additional privacy + self.__data = _AccountData.create(account_number, owner_name, initial_balance, pin) + + @property + def owner_name(self) -> str: + """Read-only access to owner name.""" + return self.__data.owner_name + + @property + def masked_account_number(self) -> str: + """Read-only access to masked account number.""" + return self.__data.get_masked_account_number() + + def __validate_pin(self, pin: str) -> bool: + """Private method for PIN validation.""" + return self.__data.validate_pin(pin) + + def transfer_to(self, other_account: 'AlternativeSecureAccount', amount: float, pin: str) -> bool: + """Transfer money to another account.""" + if not self.__validate_pin(pin): + print("Invalid PIN") + return False + + if not self.validate_transaction(amount): + print("Insufficient funds for transfer") + return False + + # Perform transfer (simplified - in real system would be atomic) + new_balance = self.__data.balance - amount + self.__data = self.__data.with_new_balance(new_balance) + + # In real system, this would also update the recipient account + print(f"Transfer of ${amount:.2f} completed") + return True + + def validate_transaction(self, amount: float) -> bool: + """Public method for transaction validation.""" + return self.__data.balance >= amount + +# Custom exception for security errors +class SecurityError(Exception): + """Raised when security validation fails.""" + pass + +# Demonstration function +def demonstrate_private_class_data_pattern(): + """Demonstrate the Private Class Data pattern with examples.""" + print("=== Private Class Data Pattern Demo ===\n") + + # Create secure bank account + account = SecureBankAccount("1234567890", "Alice Johnson", 1500.0, "9876") + + # Display initial account summary + print("Initial Account Summary:") + print(account.get_account_summary()) + print() + + # Valid transactions + print("--- Valid Transactions ---") + account.withdraw(200.0, "9876") + account.deposit(100.0, "9876") + print() + + # Invalid transactions + print("--- Invalid Transactions ---") + account.withdraw(300.0, "0000") # Wrong PIN + account.withdraw(2000.0, "9876") # Insufficient funds + print() + + # Safe validation + print("--- Transaction Validation ---") + print(f"Can withdraw $500? {account.validate_transaction(500.0)}") + print(f"Can withdraw $2000? {account.validate_transaction(2000.0)}") + print() + + # Security demonstration + print("--- Security Features ---") + print(f"Masked Account Number: {account.get_masked_account_number()}") + print(f"Owner Name: {account.get_owner_name()}") + + try: + balance = account.get_balance("9876") + print(f"Balance with correct PIN: ${balance:.2f}") + except SecurityError as e: + print(f"Security error: {e}") + + try: + account.get_balance("0000") # Wrong PIN + except SecurityError as e: + print(f"Security error with wrong PIN: {e}") + + print() + + # Alternative implementation demo + print("--- Alternative Implementation ---") + alt_account = AlternativeSecureAccount("9876543210", "Bob Smith", 2000.0, "1111") + print(f"Owner: {alt_account.owner_name}") + print(f"Masked Account: {alt_account.masked_account_number}") + alt_account.transfer_to(account, 100.0, "1111") + + # Final summaries + print("\n--- Final Account Summaries ---") + print("Main Account:") + print(account.get_account_summary()) + +# Additional example: Configuration class with private data +@dataclass(frozen=True) +class _ConfigData: + """Private configuration data.""" + database_url: str + api_key: str + max_connections: int + debug_mode: bool + created_at: float + + def get_safe_config(self) -> dict: + """Return configuration without sensitive data.""" + return { + 'max_connections': self.max_connections, + 'debug_mode': self.debug_mode, + 'created_at': time.ctime(self.created_at) + } + +class SecureConfiguration: + """Secure configuration class using private class data pattern.""" + + def __init__(self, database_url: str, api_key: str, max_connections: int = 10, debug_mode: bool = False): + self._config_data = _ConfigData( + database_url=database_url, + api_key=api_key, + max_connections=max_connections, + debug_mode=debug_mode, + created_at=time.time() + ) + + def get_database_connection_string(self, admin_key: str) -> str: + """Get database URL with admin authentication.""" + if admin_key != "admin_secret_key": + raise SecurityError("Invalid admin key") + return self._config_data.database_url + + def get_api_key(self, service_token: str) -> str: + """Get API key with service authentication.""" + if service_token != "service_token_123": + raise SecurityError("Invalid service token") + return self._config_data.api_key + + def get_public_config(self) -> dict: + """Get non-sensitive configuration data.""" + return self._config_data.get_safe_config() + + @property + def max_connections(self) -> int: + """Public read-only access to max connections.""" + return self._config_data.max_connections + + @property + def debug_mode(self) -> bool: + """Public read-only access to debug mode.""" + return self._config_data.debug_mode + +if __name__ == "__main__": + demonstrate_private_class_data_pattern() + + print("\n=== Configuration Example ===") + config = SecureConfiguration("postgresql://localhost:5432/db", "secret_api_key_123", 20, True) + + print("Public config:", config.get_public_config()) + print(f"Max connections: {config.max_connections}") + print(f"Debug mode: {config.debug_mode}") + + # Secure access to sensitive data + try: + db_url = config.get_database_connection_string("admin_secret_key") + print("Database URL obtained successfully (not displaying for security)") + except SecurityError as e: + print(f"Security error: {e}") + +""" +Benefits of Private Class Data Pattern in Python: + +1. ENCAPSULATION: Sensitive data is encapsulated in private data classes +2. IMMUTABILITY: Using @dataclass(frozen=True) ensures immutability +3. CONTROLLED ACCESS: All data access goes through validated methods +4. SECURITY: No direct access to sensitive information like PINs or API keys +5. CONSISTENCY: State changes are atomic and consistent +6. THREAD SAFETY: Immutable objects are inherently thread-safe +7. PYTHONIC: Uses dataclasses, properties, and name mangling effectively + +Python-specific features used: +- @dataclass(frozen=True) for immutable data classes +- Property decorators for controlled access +- Name mangling (__attribute) for additional privacy +- Type hints for better code documentation +- Custom exceptions for security validation + +Use Cases: +- Financial systems with sensitive account data +- User profiles with personal information +- Configuration classes with secrets and credentials +- Any class where data integrity and security is critical +""" diff --git a/src/content/design-pattern/code/prototype/solution.cpp b/src/content/design-pattern/code/prototype/solution.cpp new file mode 100644 index 0000000..620b8c9 --- /dev/null +++ b/src/content/design-pattern/code/prototype/solution.cpp @@ -0,0 +1,509 @@ +#include +#include +#include +#include +#include +#include +#include + +/** + * Prototype Pattern - Graphics Shape System + * Creates complex graphics objects by cloning prototypes, demonstrating deep copying + */ + +// Forward declarations +class Shape; + +// Prototype interface +class ShapePrototype { +public: + virtual ~ShapePrototype() = default; + virtual std::unique_ptr clone() const = 0; + virtual void display() const = 0; + virtual std::string getType() const = 0; + virtual void draw() const = 0; +}; + +// Complex helper classes that need deep copying +class Point { +public: + double x, y; + + Point(double x = 0, double y = 0) : x(x), y(y) {} + + Point(const Point& other) : x(other.x), y(other.y) {} + + std::string toString() const { + return "(" + std::to_string(x) + ", " + std::to_string(y) + ")"; + } +}; + +class Color { +public: + int r, g, b, alpha; + + Color(int r = 255, int g = 255, int b = 255, int alpha = 255) + : r(r), g(g), b(b), alpha(alpha) {} + + Color(const Color& other) : r(other.r), g(other.g), b(other.b), alpha(other.alpha) {} + + std::string toString() const { + return "RGB(" + std::to_string(r) + ", " + std::to_string(g) + + ", " + std::to_string(b) + ", " + std::to_string(alpha) + ")"; + } +}; + +class Style { +public: + Color fillColor; + Color strokeColor; + double strokeWidth; + std::string pattern; + std::map customProperties; + + Style() : fillColor(200, 200, 200), strokeColor(0, 0, 0), strokeWidth(1.0), pattern("solid") {} + + // Copy constructor for deep copying + Style(const Style& other) + : fillColor(other.fillColor), strokeColor(other.strokeColor), + strokeWidth(other.strokeWidth), pattern(other.pattern), + customProperties(other.customProperties) {} + + void setProperty(const std::string& key, const std::string& value) { + customProperties[key] = value; + } + + std::string toString() const { + return "Style{fill: " + fillColor.toString() + + ", stroke: " + strokeColor.toString() + + ", width: " + std::to_string(strokeWidth) + + ", pattern: " + pattern + + ", custom: " + std::to_string(customProperties.size()) + " props}"; + } +}; + +// Concrete prototype implementations +class Circle : public ShapePrototype { +private: + Point center; + double radius; + Style style; + std::string id; + std::vector tags; + +public: + Circle(const Point& center, double radius, const std::string& id = "circle") + : center(center), radius(radius), id(id) { + + // Default style + style.fillColor = Color(100, 150, 255, 200); + style.strokeColor = Color(0, 0, 255, 255); + style.strokeWidth = 2.0; + + tags.push_back("geometric"); + tags.push_back("curved"); + } + + // Copy constructor for cloning + Circle(const Circle& other) + : center(other.center), radius(other.radius), style(other.style), + id(other.id + "_copy"), tags(other.tags) {} + + std::unique_ptr clone() const override { + std::cout << "🔄 Cloning Circle: " << id << std::endl; + return std::make_unique(*this); + } + + void display() const override { + std::cout << "⭕ CIRCLE" << std::endl; + std::cout << "├─ ID: " << id << std::endl; + std::cout << "├─ Center: " << center.toString() << std::endl; + std::cout << "├─ Radius: " << radius << std::endl; + std::cout << "├─ Style: " << style.toString() << std::endl; + std::cout << "└─ Tags: "; + for (size_t i = 0; i < tags.size(); ++i) { + std::cout << tags[i]; + if (i < tags.size() - 1) std::cout << ", "; + } + std::cout << std::endl; + } + + std::string getType() const override { return "Circle"; } + + void draw() const override { + std::cout << "🎨 Drawing circle at " << center.toString() + << " with radius " << radius << std::endl; + std::cout << " Using " << style.toString() << std::endl; + } + + // Customization methods + void setCenter(const Point& newCenter) { center = newCenter; } + void setRadius(double newRadius) { radius = newRadius; } + void setId(const std::string& newId) { id = newId; } + void setFillColor(const Color& color) { style.fillColor = color; } + void setStrokeColor(const Color& color) { style.strokeColor = color; } + void addTag(const std::string& tag) { tags.push_back(tag); } + Style& getStyle() { return style; } +}; + +class Rectangle : public ShapePrototype { +private: + Point topLeft; + double width, height; + Style style; + std::string id; + std::vector tags; + bool rounded; + double cornerRadius; + +public: + Rectangle(const Point& topLeft, double width, double height, const std::string& id = "rectangle") + : topLeft(topLeft), width(width), height(height), id(id), rounded(false), cornerRadius(0.0) { + + // Default style + style.fillColor = Color(255, 200, 100, 180); + style.strokeColor = Color(200, 100, 0, 255); + style.strokeWidth = 1.5; + + tags.push_back("geometric"); + tags.push_back("angular"); + } + + // Copy constructor for cloning + Rectangle(const Rectangle& other) + : topLeft(other.topLeft), width(other.width), height(other.height), + style(other.style), id(other.id + "_copy"), tags(other.tags), + rounded(other.rounded), cornerRadius(other.cornerRadius) {} + + std::unique_ptr clone() const override { + std::cout << "🔄 Cloning Rectangle: " << id << std::endl; + return std::make_unique(*this); + } + + void display() const override { + std::cout << "▭ RECTANGLE" << std::endl; + std::cout << "├─ ID: " << id << std::endl; + std::cout << "├─ Top-Left: " << topLeft.toString() << std::endl; + std::cout << "├─ Dimensions: " << width << " x " << height << std::endl; + std::cout << "├─ Rounded: " << (rounded ? "Yes" : "No"); + if (rounded) std::cout << " (radius: " << cornerRadius << ")"; + std::cout << std::endl; + std::cout << "├─ Style: " << style.toString() << std::endl; + std::cout << "└─ Tags: "; + for (size_t i = 0; i < tags.size(); ++i) { + std::cout << tags[i]; + if (i < tags.size() - 1) std::cout << ", "; + } + std::cout << std::endl; + } + + std::string getType() const override { return "Rectangle"; } + + void draw() const override { + std::cout << "🎨 Drawing rectangle at " << topLeft.toString() + << " with size " << width << "x" << height << std::endl; + std::cout << " Using " << style.toString() << std::endl; + if (rounded) { + std::cout << " With rounded corners (radius: " << cornerRadius << ")" << std::endl; + } + } + + // Customization methods + void setPosition(const Point& newTopLeft) { topLeft = newTopLeft; } + void setDimensions(double newWidth, double newHeight) { width = newWidth; height = newHeight; } + void setId(const std::string& newId) { id = newId; } + void setFillColor(const Color& color) { style.fillColor = color; } + void setStrokeColor(const Color& color) { style.strokeColor = color; } + void addTag(const std::string& tag) { tags.push_back(tag); } + void setRounded(bool isRounded, double radius = 5.0) { + rounded = isRounded; + cornerRadius = radius; + } + Style& getStyle() { return style; } +}; + +class Polygon : public ShapePrototype { +private: + std::vector vertices; + Style style; + std::string id; + std::vector tags; + bool closed; + +public: + Polygon(const std::vector& vertices, const std::string& id = "polygon") + : vertices(vertices), id(id), closed(true) { + + // Default style + style.fillColor = Color(100, 255, 100, 150); + style.strokeColor = Color(0, 200, 0, 255); + style.strokeWidth = 2.0; + style.pattern = "dashed"; + + tags.push_back("geometric"); + tags.push_back("multi-sided"); + } + + // Copy constructor for cloning + Polygon(const Polygon& other) + : vertices(other.vertices), style(other.style), id(other.id + "_copy"), + tags(other.tags), closed(other.closed) {} + + std::unique_ptr clone() const override { + std::cout << "🔄 Cloning Polygon: " << id << " (" << vertices.size() << " vertices)" << std::endl; + return std::make_unique(*this); + } + + void display() const override { + std::cout << "🔺 POLYGON" << std::endl; + std::cout << "├─ ID: " << id << std::endl; + std::cout << "├─ Vertices: " << vertices.size() << std::endl; + for (size_t i = 0; i < std::min(vertices.size(), size_t(3)); ++i) { + std::cout << "│ ├─ " << vertices[i].toString() << std::endl; + } + if (vertices.size() > 3) { + std::cout << "│ └─ ... (" << (vertices.size() - 3) << " more)" << std::endl; + } + std::cout << "├─ Closed: " << (closed ? "Yes" : "No") << std::endl; + std::cout << "├─ Style: " << style.toString() << std::endl; + std::cout << "└─ Tags: "; + for (size_t i = 0; i < tags.size(); ++i) { + std::cout << tags[i]; + if (i < tags.size() - 1) std::cout << ", "; + } + std::cout << std::endl; + } + + std::string getType() const override { return "Polygon"; } + + void draw() const override { + std::cout << "🎨 Drawing polygon with " << vertices.size() << " vertices" << std::endl; + std::cout << " Using " << style.toString() << std::endl; + std::cout << " Shape is " << (closed ? "closed" : "open") << std::endl; + } + + // Customization methods + void addVertex(const Point& vertex) { vertices.push_back(vertex); } + void setId(const std::string& newId) { id = newId; } + void setFillColor(const Color& color) { style.fillColor = color; } + void setStrokeColor(const Color& color) { style.strokeColor = color; } + void addTag(const std::string& tag) { tags.push_back(tag); } + void setClosed(bool isClosed) { closed = isClosed; } + Style& getStyle() { return style; } + const std::vector& getVertices() const { return vertices; } +}; + +// Factory for managing shape prototypes +class ShapeFactory { +private: + std::unordered_map> prototypes; + +public: + ShapeFactory() { + initializePrototypes(); + } + + void initializePrototypes() { + std::cout << "🏭 Initializing shape prototypes..." << std::endl; + + // Create template shapes + auto circleTemplate = std::make_unique(Point(50, 50), 25, "template_circle"); + circleTemplate->getStyle().setProperty("template", "true"); + circleTemplate->addTag("template"); + + auto rectTemplate = std::make_unique(Point(10, 10), 100, 60, "template_rectangle"); + rectTemplate->setRounded(true, 8.0); + rectTemplate->getStyle().setProperty("template", "true"); + rectTemplate->addTag("template"); + + std::vector trianglePoints = {Point(0, 0), Point(50, 0), Point(25, 40)}; + auto triangleTemplate = std::make_unique(trianglePoints, "template_triangle"); + triangleTemplate->getStyle().setProperty("template", "true"); + triangleTemplate->addTag("template"); + triangleTemplate->addTag("triangle"); + + // Register prototypes + prototypes["circle"] = std::move(circleTemplate); + prototypes["rectangle"] = std::move(rectTemplate); + prototypes["triangle"] = std::move(triangleTemplate); + + std::cout << "✅ Prototypes initialized: "; + for (const auto& pair : prototypes) { + std::cout << pair.first << " "; + } + std::cout << std::endl; + } + + std::unique_ptr createShape(const std::string& type) { + auto it = prototypes.find(type); + if (it != prototypes.end()) { + return it->second->clone(); + } + throw std::invalid_argument("Unknown shape type: " + type); + } + + void registerPrototype(const std::string& name, std::unique_ptr prototype) { + prototypes[name] = std::move(prototype); + std::cout << "📝 Registered new prototype: " << name << std::endl; + } + + std::vector getAvailableTypes() const { + std::vector types; + for (const auto& pair : prototypes) { + types.push_back(pair.first); + } + return types; + } +}; + +// Demonstrate deep copying behavior +void demonstrateDeepCopy() { + std::cout << "\n--- Deep Copy Demonstration ---" << std::endl; + + // Create original shape + Circle original(Point(100, 100), 50, "original_circle"); + original.setFillColor(Color(255, 0, 0, 200)); + original.getStyle().setProperty("category", "special"); + original.addTag("original"); + + std::cout << "Original before cloning:" << std::endl; + original.display(); + + // Clone the shape + auto cloned = original.clone(); + auto* clonedCircle = dynamic_cast(cloned.get()); + + if (clonedCircle) { + // Modify the clone + clonedCircle->setCenter(Point(200, 200)); + clonedCircle->setRadius(75); + clonedCircle->setFillColor(Color(0, 255, 0, 200)); + clonedCircle->getStyle().setProperty("category", "modified"); + clonedCircle->addTag("cloned"); + clonedCircle->setId("modified_circle"); + + std::cout << "\nOriginal after clone modification:" << std::endl; + original.display(); + + std::cout << "\nCloned shape after modification:" << std::endl; + clonedCircle->display(); + + std::cout << "\n✅ Deep copy verification: Original unchanged after clone modification" << std::endl; + } +} + +int main() { + std::cout << "=== Prototype Pattern Demo - Graphics Shape System ===\n" << std::endl; + + try { + ShapeFactory factory; + + std::cout << "\n--- Creating Shapes from Prototypes ---" << std::endl; + + // Create various shapes using prototypes + auto circle1 = factory.createShape("circle"); + auto rect1 = factory.createShape("rectangle"); + auto triangle1 = factory.createShape("triangle"); + + // Customize the created shapes + if (auto* circle = dynamic_cast(circle1.get())) { + circle->setId("user_circle"); + circle->setCenter(Point(150, 150)); + circle->setRadius(40); + circle->setFillColor(Color(255, 100, 100, 180)); + circle->addTag("customized"); + } + + if (auto* rect = dynamic_cast(rect1.get())) { + rect->setId("user_rectangle"); + rect->setPosition(Point(50, 200)); + rect->setDimensions(120, 80); + rect->setFillColor(Color(100, 100, 255, 200)); + rect->addTag("customized"); + } + + if (auto* triangle = dynamic_cast(triangle1.get())) { + triangle->setId("user_triangle"); + triangle->setFillColor(Color(255, 255, 100, 150)); + triangle->addTag("customized"); + } + + std::cout << "\nDisplaying created shapes:" << std::endl; + circle1->display(); + std::cout << std::endl; + rect1->display(); + std::cout << std::endl; + triangle1->display(); + + std::cout << "\n--- Drawing Shapes ---" << std::endl; + circle1->draw(); + rect1->draw(); + triangle1->draw(); + + // Demonstrate deep copying + demonstrateDeepCopy(); + + std::cout << "\n--- Custom Prototype Registration ---" << std::endl; + + // Create a custom star shape + std::vector starPoints = { + Point(50, 20), Point(60, 40), Point(80, 40), Point(65, 55), + Point(70, 75), Point(50, 60), Point(30, 75), Point(35, 55), + Point(20, 40), Point(40, 40) + }; + auto starPrototype = std::make_unique(starPoints, "template_star"); + starPrototype->setFillColor(Color(255, 215, 0, 200)); // Gold color + starPrototype->getStyle().setProperty("template", "true"); + starPrototype->addTag("template"); + starPrototype->addTag("star"); + + factory.registerPrototype("star", std::move(starPrototype)); + + // Create a star using the new prototype + auto star = factory.createShape("star"); + if (auto* starShape = dynamic_cast(star.get())) { + starShape->setId("golden_star"); + starShape->addTag("special"); + } + + std::cout << "Shape created from custom prototype:" << std::endl; + star->display(); + std::cout << std::endl; + star->draw(); + + std::cout << "\n--- Performance Comparison ---" << std::endl; + + auto availableTypes = factory.getAvailableTypes(); + std::cout << "Available shape types: "; + for (const auto& type : availableTypes) { + std::cout << type << " "; + } + std::cout << std::endl; + + // Measure cloning performance + auto start = std::chrono::high_resolution_clock::now(); + std::vector> shapes; + + for (int i = 0; i < 10000; ++i) { + std::string type = availableTypes[i % availableTypes.size()]; + shapes.push_back(factory.createShape(type)); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + std::cout << "📊 Created " << shapes.size() << " shapes in " + << duration.count() << " microseconds using prototypes" << std::endl; + std::cout << "⚡ Average time per shape: " + << (double(duration.count()) / shapes.size()) << " microseconds" << std::endl; + + std::cout << "\n✅ Prototype pattern successfully demonstrated!" << std::endl; + std::cout << "Benefits: Fast object creation, complex initialization reuse, polymorphic cloning" << std::endl; + + } catch (const std::exception& e) { + std::cerr << "❌ Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/content/design-pattern/code/prototype/solution.java b/src/content/design-pattern/code/prototype/solution.java new file mode 100644 index 0000000..8c7c7d3 --- /dev/null +++ b/src/content/design-pattern/code/prototype/solution.java @@ -0,0 +1,400 @@ +/** + * Prototype Pattern - Document Management System Example + * Creates new objects by cloning existing prototypes, supporting both shallow and deep copying + */ + +import java.util.*; + +// Prototype interface +interface DocumentPrototype extends Cloneable { + DocumentPrototype clone(); + void display(); + String getType(); +} + +// Complex object that needs deep cloning +class DocumentMetadata implements Cloneable { + private String author; + private Date creationDate; + private Map tags; + private List reviewers; + + public DocumentMetadata(String author) { + this.author = author; + this.creationDate = new Date(); + this.tags = new HashMap<>(); + this.reviewers = new ArrayList<>(); + } + + // Copy constructor for deep cloning + public DocumentMetadata(DocumentMetadata other) { + this.author = other.author; + this.creationDate = new Date(other.creationDate.getTime()); // Deep copy date + this.tags = new HashMap<>(other.tags); // Deep copy map + this.reviewers = new ArrayList<>(other.reviewers); // Deep copy list + } + + @Override + public DocumentMetadata clone() { + return new DocumentMetadata(this); + } + + // Getters and setters + public String getAuthor() { return author; } + public void setAuthor(String author) { this.author = author; } + + public Date getCreationDate() { return creationDate; } + public void setCreationDate(Date creationDate) { this.creationDate = creationDate; } + + public Map getTags() { return tags; } + public void addTag(String key, String value) { this.tags.put(key, value); } + + public List getReviewers() { return reviewers; } + public void addReviewer(String reviewer) { this.reviewers.add(reviewer); } + + @Override + public String toString() { + return String.format("Author: %s, Created: %s, Tags: %d, Reviewers: %d", + author, creationDate, tags.size(), reviewers.size()); + } +} + +// Concrete prototype implementations +class TextDocument implements DocumentPrototype { + private String title; + private String content; + private DocumentMetadata metadata; + private String formatting; // Font, size, etc. + + public TextDocument(String title, String content, String author) { + this.title = title; + this.content = content; + this.metadata = new DocumentMetadata(author); + this.formatting = "Arial, 12pt"; + + // Add default tags + metadata.addTag("type", "text"); + metadata.addTag("format", "plain"); + } + + // Copy constructor for cloning + private TextDocument(TextDocument other) { + this.title = other.title; + this.content = other.content; + this.metadata = other.metadata.clone(); // Deep copy metadata + this.formatting = other.formatting; + } + + @Override + public DocumentPrototype clone() { + System.out.println("🔄 Cloning TextDocument: " + title); + return new TextDocument(this); + } + + @Override + public void display() { + System.out.println("📄 TEXT DOCUMENT"); + System.out.println("├─ Title: " + title); + System.out.println("├─ Content: " + content.substring(0, Math.min(50, content.length())) + "..."); + System.out.println("├─ Formatting: " + formatting); + System.out.println("└─ Metadata: " + metadata); + } + + @Override + public String getType() { return "TextDocument"; } + + // Specific methods for customization + public void setTitle(String title) { this.title = title; } + public void setContent(String content) { this.content = content; } + public void setFormatting(String formatting) { this.formatting = formatting; } + public DocumentMetadata getMetadata() { return metadata; } +} + +class SpreadsheetDocument implements DocumentPrototype { + private String title; + private List> data; + private DocumentMetadata metadata; + private Map formulas; + + public SpreadsheetDocument(String title, String author) { + this.title = title; + this.data = new ArrayList<>(); + this.metadata = new DocumentMetadata(author); + this.formulas = new HashMap<>(); + + // Initialize with sample data + List headerRow = Arrays.asList("Name", "Age", "Department", "Salary"); + data.add(headerRow); + + // Add default tags + metadata.addTag("type", "spreadsheet"); + metadata.addTag("format", "xlsx"); + } + + // Copy constructor for cloning + private SpreadsheetDocument(SpreadsheetDocument other) { + this.title = other.title; + this.metadata = other.metadata.clone(); // Deep copy metadata + this.formulas = new HashMap<>(other.formulas); // Deep copy formulas + + // Deep copy data structure + this.data = new ArrayList<>(); + for (List row : other.data) { + this.data.add(new ArrayList<>(row)); + } + } + + @Override + public DocumentPrototype clone() { + System.out.println("🔄 Cloning SpreadsheetDocument: " + title); + return new SpreadsheetDocument(this); + } + + @Override + public void display() { + System.out.println("📊 SPREADSHEET DOCUMENT"); + System.out.println("├─ Title: " + title); + System.out.println("├─ Rows: " + data.size()); + System.out.println("├─ Columns: " + (data.isEmpty() ? 0 : data.get(0).size())); + System.out.println("├─ Formulas: " + formulas.size()); + System.out.println("└─ Metadata: " + metadata); + } + + @Override + public String getType() { return "SpreadsheetDocument"; } + + public void setTitle(String title) { this.title = title; } + public void addRow(List row) { this.data.add(row); } + public void addFormula(String cell, String formula) { this.formulas.put(cell, formula); } + public DocumentMetadata getMetadata() { return metadata; } +} + +class PresentationDocument implements DocumentPrototype { + private String title; + private List slides; + private DocumentMetadata metadata; + private String theme; + private Map slideNotes; + + public PresentationDocument(String title, String author) { + this.title = title; + this.slides = new ArrayList<>(); + this.metadata = new DocumentMetadata(author); + this.theme = "Professional Blue"; + this.slideNotes = new HashMap<>(); + + // Add default slides + slides.add("Title Slide"); + slides.add("Agenda"); + slides.add("Content"); + + // Add default tags + metadata.addTag("type", "presentation"); + metadata.addTag("format", "pptx"); + } + + // Copy constructor for cloning + private PresentationDocument(PresentationDocument other) { + this.title = other.title; + this.metadata = other.metadata.clone(); // Deep copy metadata + this.theme = other.theme; + this.slides = new ArrayList<>(other.slides); // Deep copy slides + this.slideNotes = new HashMap<>(other.slideNotes); // Deep copy notes + } + + @Override + public DocumentPrototype clone() { + System.out.println("🔄 Cloning PresentationDocument: " + title); + return new PresentationDocument(this); + } + + @Override + public void display() { + System.out.println("🎨 PRESENTATION DOCUMENT"); + System.out.println("├─ Title: " + title); + System.out.println("├─ Slides: " + slides.size()); + System.out.println("├─ Theme: " + theme); + System.out.println("├─ Notes: " + slideNotes.size() + " slides have notes"); + System.out.println("└─ Metadata: " + metadata); + } + + @Override + public String getType() { return "PresentationDocument"; } + + public void setTitle(String title) { this.title = title; } + public void addSlide(String slide) { this.slides.add(slide); } + public void setTheme(String theme) { this.theme = theme; } + public void addSlideNote(int slideIndex, String note) { this.slideNotes.put(slideIndex, note); } + public DocumentMetadata getMetadata() { return metadata; } +} + +// Document factory using prototypes +class DocumentFactory { + private Map prototypes; + + public DocumentFactory() { + this.prototypes = new HashMap<>(); + initializePrototypes(); + } + + private void initializePrototypes() { + System.out.println("🏭 Initializing document prototypes..."); + + // Create template documents + TextDocument textTemplate = new TextDocument("Sample Text Document", + "This is a sample text document that can be used as a template for creating new documents.", + "System"); + textTemplate.getMetadata().addTag("template", "true"); + textTemplate.getMetadata().addReviewer("Admin"); + + SpreadsheetDocument spreadsheetTemplate = new SpreadsheetDocument("Sample Spreadsheet", "System"); + spreadsheetTemplate.getMetadata().addTag("template", "true"); + spreadsheetTemplate.addRow(Arrays.asList("John Doe", "30", "Engineering", "75000")); + spreadsheetTemplate.addFormula("E2", "D2*0.1"); + + PresentationDocument presentationTemplate = new PresentationDocument("Sample Presentation", "System"); + presentationTemplate.getMetadata().addTag("template", "true"); + presentationTemplate.addSlideNote(0, "Welcome everyone to the presentation"); + + // Register prototypes + prototypes.put("text", textTemplate); + prototypes.put("spreadsheet", spreadsheetTemplate); + prototypes.put("presentation", presentationTemplate); + + System.out.println("✅ Prototypes initialized: " + prototypes.keySet()); + } + + public DocumentPrototype createDocument(String type) { + DocumentPrototype prototype = prototypes.get(type.toLowerCase()); + if (prototype != null) { + return prototype.clone(); + } + throw new IllegalArgumentException("Unknown document type: " + type); + } + + public void registerPrototype(String name, DocumentPrototype prototype) { + prototypes.put(name, prototype); + System.out.println("📝 Registered new prototype: " + name); + } + + public Set getAvailableTypes() { + return prototypes.keySet(); + } +} + +// Client code demonstrating the pattern +public class PrototypePatternDemo { + public static void main(String[] args) { + System.out.println("=== Prototype Pattern Demo - Document Management ===\n"); + + DocumentFactory factory = new DocumentFactory(); + + System.out.println("\n--- Creating Documents from Prototypes ---"); + + // Create various documents using prototypes + DocumentPrototype doc1 = factory.createDocument("text"); + if (doc1 instanceof TextDocument) { + TextDocument textDoc = (TextDocument) doc1; + textDoc.setTitle("Project Requirements"); + textDoc.setContent("This document outlines the requirements for the new project management system."); + textDoc.getMetadata().setAuthor("Alice Johnson"); + textDoc.getMetadata().addReviewer("Bob Smith"); + } + + DocumentPrototype doc2 = factory.createDocument("spreadsheet"); + if (doc2 instanceof SpreadsheetDocument) { + SpreadsheetDocument spreadsheet = (SpreadsheetDocument) doc2; + spreadsheet.setTitle("Employee Database"); + spreadsheet.addRow(Arrays.asList("Jane Smith", "28", "Marketing", "65000")); + spreadsheet.addRow(Arrays.asList("Mike Johnson", "35", "Sales", "70000")); + spreadsheet.getMetadata().setAuthor("HR Department"); + } + + DocumentPrototype doc3 = factory.createDocument("presentation"); + if (doc3 instanceof PresentationDocument) { + PresentationDocument presentation = (PresentationDocument) doc3; + presentation.setTitle("Q4 Business Review"); + presentation.addSlide("Financial Summary"); + presentation.addSlide("Market Analysis"); + presentation.setTheme("Corporate Green"); + presentation.getMetadata().setAuthor("Executive Team"); + } + + System.out.println("\n--- Displaying Created Documents ---"); + + doc1.display(); + System.out.println(); + doc2.display(); + System.out.println(); + doc3.display(); + + System.out.println("\n--- Testing Deep Copy Behavior ---"); + + // Test that cloning creates independent objects + DocumentPrototype originalText = factory.createDocument("text"); + DocumentPrototype clonedText = originalText.clone(); + + if (originalText instanceof TextDocument && clonedText instanceof TextDocument) { + TextDocument original = (TextDocument) originalText; + TextDocument cloned = (TextDocument) clonedText; + + System.out.println("Original document before modification:"); + original.display(); + + // Modify the cloned document + cloned.setTitle("Modified Clone"); + cloned.getMetadata().setAuthor("Different Author"); + cloned.getMetadata().addTag("modified", "true"); + cloned.getMetadata().addReviewer("New Reviewer"); + + System.out.println("\nOriginal document after clone modification:"); + original.display(); + System.out.println("\nCloned document after modification:"); + cloned.display(); + + System.out.println("\n✅ Deep copy verification: Original unchanged after clone modification"); + } + + System.out.println("\n--- Custom Prototype Registration ---"); + + // Create and register a custom prototype + TextDocument customTemplate = new TextDocument("Meeting Notes Template", + "Date: [DATE]\nAttendees: [ATTENDEES]\nAgenda:\n1. [ITEM1]\n2. [ITEM2]\nAction Items:\n- [ACTION1]", + "Template System"); + customTemplate.getMetadata().addTag("category", "meeting"); + customTemplate.setFormatting("Courier New, 11pt"); + + factory.registerPrototype("meeting-notes", customTemplate); + + // Use the custom prototype + DocumentPrototype meetingDoc = factory.createDocument("meeting-notes"); + if (meetingDoc instanceof TextDocument) { + TextDocument meeting = (TextDocument) meetingDoc; + meeting.setTitle("Weekly Team Meeting - March 15"); + meeting.getMetadata().setAuthor("Project Manager"); + } + + System.out.println("\nDocument created from custom prototype:"); + meetingDoc.display(); + + System.out.println("\n--- Performance Comparison ---"); + + System.out.println("Available document types: " + factory.getAvailableTypes()); + + // Simulate creating multiple documents quickly using prototypes + long startTime = System.currentTimeMillis(); + List documents = new ArrayList<>(); + + for (int i = 0; i < 100; i++) { + documents.add(factory.createDocument("text")); + documents.add(factory.createDocument("spreadsheet")); + documents.add(factory.createDocument("presentation")); + } + + long endTime = System.currentTimeMillis(); + System.out.println("📊 Created " + documents.size() + " documents in " + (endTime - startTime) + "ms using prototypes"); + + System.out.println("\n✅ Prototype pattern successfully demonstrated!"); + System.out.println("Benefits: Fast object creation, reduced initialization cost, template-based creation"); + } +} diff --git a/src/content/design-pattern/code/prototype/solution.py b/src/content/design-pattern/code/prototype/solution.py new file mode 100644 index 0000000..fdda2df --- /dev/null +++ b/src/content/design-pattern/code/prototype/solution.py @@ -0,0 +1,477 @@ +""" +Prototype Pattern - Game Character Creation System +Creates new objects by cloning existing prototypes with deep and shallow copy demonstrations +""" + +import copy +from abc import ABC, abstractmethod +from typing import Dict, List, Any +import time + +class CharacterPrototype(ABC): + """Abstract prototype interface for game characters""" + + @abstractmethod + def clone(self): + """Create a copy of this character""" + pass + + @abstractmethod + def display(self): + """Display character information""" + pass + + @abstractmethod + def get_type(self): + """Get character type""" + pass + +class Equipment: + """Complex object representing character equipment""" + + def __init__(self): + self.weapons = [] + self.armor = {} + self.accessories = [] + self.inventory = {} + + def add_weapon(self, weapon): + self.weapons.append(weapon) + + def set_armor(self, slot, armor): + self.armor[slot] = armor + + def add_accessory(self, accessory): + self.accessories.append(accessory) + + def add_to_inventory(self, item, quantity): + if item in self.inventory: + self.inventory[item] += quantity + else: + self.inventory[item] = quantity + + def __str__(self): + return (f"Weapons: {len(self.weapons)}, Armor: {len(self.armor)}, " + f"Accessories: {len(self.accessories)}, Inventory: {len(self.inventory)} items") + +class Stats: + """Character statistics that need deep copying""" + + def __init__(self, strength=10, agility=10, intelligence=10, vitality=10): + self.base_stats = { + 'strength': strength, + 'agility': agility, + 'intelligence': intelligence, + 'vitality': vitality + } + self.modifiers = {} + self.experience_history = [] + + def add_modifier(self, stat, modifier): + if stat in self.modifiers: + self.modifiers[stat] += modifier + else: + self.modifiers[stat] = modifier + + def add_experience(self, amount, source): + self.experience_history.append({'amount': amount, 'source': source, 'timestamp': time.time()}) + + def get_effective_stat(self, stat): + base = self.base_stats.get(stat, 0) + modifier = self.modifiers.get(stat, 0) + return base + modifier + + def __str__(self): + stats_str = ', '.join([f"{k}: {self.get_effective_stat(k)}" for k in self.base_stats]) + return f"{stats_str} (XP History: {len(self.experience_history)} entries)" + +class WarriorCharacter(CharacterPrototype): + """Concrete warrior character prototype""" + + def __init__(self, name="Unnamed Warrior"): + self.name = name + self.character_class = "Warrior" + self.level = 1 + self.stats = Stats(strength=18, agility=12, intelligence=8, vitality=16) + self.equipment = Equipment() + self.skills = [] + self.achievements = [] + + # Initialize default equipment + self.equipment.add_weapon("Iron Sword") + self.equipment.set_armor("chest", "Chainmail") + self.equipment.set_armor("legs", "Iron Greaves") + self.equipment.add_to_inventory("Health Potion", 5) + + # Default skills + self.skills = ["Sword Mastery", "Shield Block", "Rage"] + + # Add some experience + self.stats.add_experience(100, "Character Creation") + + def clone(self): + """Create a deep copy of this warrior""" + print(f"🔄 Cloning Warrior: {self.name}") + + # Create new warrior with same name + cloned = WarriorCharacter(f"{self.name} (Clone)") + + # Deep copy all complex objects + cloned.level = self.level + cloned.stats = copy.deepcopy(self.stats) + cloned.equipment = copy.deepcopy(self.equipment) + cloned.skills = copy.deepcopy(self.skills) + cloned.achievements = copy.deepcopy(self.achievements) + + return cloned + + def display(self): + print("⚔️ WARRIOR CHARACTER") + print(f"├─ Name: {self.name}") + print(f"├─ Class: {self.character_class}") + print(f"├─ Level: {self.level}") + print(f"├─ Stats: {self.stats}") + print(f"├─ Equipment: {self.equipment}") + print(f"├─ Skills: {', '.join(self.skills)}") + print(f"└─ Achievements: {len(self.achievements)} unlocked") + + def get_type(self): + return "Warrior" + + def level_up(self): + self.level += 1 + self.stats.base_stats['strength'] += 2 + self.stats.base_stats['vitality'] += 2 + self.stats.add_experience(self.level * 100, f"Level {self.level}") + + if self.level == 5: + self.skills.append("Berserker Rage") + elif self.level == 10: + self.skills.append("Guardian Shield") + +class MageCharacter(CharacterPrototype): + """Concrete mage character prototype""" + + def __init__(self, name="Unnamed Mage"): + self.name = name + self.character_class = "Mage" + self.level = 1 + self.stats = Stats(strength=6, agility=10, intelligence=18, vitality=12) + self.equipment = Equipment() + self.skills = [] + self.spells = [] + self.mana_pool = 100 + self.achievements = [] + + # Initialize default equipment + self.equipment.add_weapon("Oak Staff") + self.equipment.set_armor("chest", "Apprentice Robes") + self.equipment.add_accessory("Mana Crystal") + self.equipment.add_to_inventory("Mana Potion", 10) + + # Default skills and spells + self.skills = ["Spell Casting", "Mana Control", "Arcane Knowledge"] + self.spells = ["Fireball", "Magic Missile", "Shield"] + + # Add some experience + self.stats.add_experience(150, "Character Creation") + + def clone(self): + """Create a deep copy of this mage""" + print(f"🔄 Cloning Mage: {self.name}") + + # Create new mage with same name + cloned = MageCharacter(f"{self.name} (Clone)") + + # Deep copy all complex objects + cloned.level = self.level + cloned.stats = copy.deepcopy(self.stats) + cloned.equipment = copy.deepcopy(self.equipment) + cloned.skills = copy.deepcopy(self.skills) + cloned.spells = copy.deepcopy(self.spells) + cloned.mana_pool = self.mana_pool + cloned.achievements = copy.deepcopy(self.achievements) + + return cloned + + def display(self): + print("🧙 MAGE CHARACTER") + print(f"├─ Name: {self.name}") + print(f"├─ Class: {self.character_class}") + print(f"├─ Level: {self.level}") + print(f"├─ Stats: {self.stats}") + print(f"├─ Equipment: {self.equipment}") + print(f"├─ Skills: {', '.join(self.skills)}") + print(f"├─ Spells: {', '.join(self.spells)}") + print(f"├─ Mana: {self.mana_pool}") + print(f"└─ Achievements: {len(self.achievements)} unlocked") + + def get_type(self): + return "Mage" + + def level_up(self): + self.level += 1 + self.stats.base_stats['intelligence'] += 2 + self.stats.base_stats['vitality'] += 1 + self.mana_pool += 20 + self.stats.add_experience(self.level * 100, f"Level {self.level}") + + if self.level == 3: + self.spells.append("Lightning Bolt") + elif self.level == 5: + self.spells.append("Teleport") + elif self.level == 10: + self.spells.append("Meteor") + +class RogueCharacter(CharacterPrototype): + """Concrete rogue character prototype""" + + def __init__(self, name="Unnamed Rogue"): + self.name = name + self.character_class = "Rogue" + self.level = 1 + self.stats = Stats(strength=12, agility=18, intelligence=14, vitality=10) + self.equipment = Equipment() + self.skills = [] + self.stealth_level = 50 + self.achievements = [] + + # Initialize default equipment + self.equipment.add_weapon("Steel Dagger") + self.equipment.add_weapon("Throwing Knives") + self.equipment.set_armor("chest", "Leather Armor") + self.equipment.add_accessory("Lockpicks") + self.equipment.add_to_inventory("Smoke Bomb", 3) + + # Default skills + self.skills = ["Stealth", "Lockpicking", "Backstab", "Trap Detection"] + + # Add some experience + self.stats.add_experience(120, "Character Creation") + + def clone(self): + """Create a deep copy of this rogue""" + print(f"🔄 Cloning Rogue: {self.name}") + + # Create new rogue with same name + cloned = RogueCharacter(f"{self.name} (Clone)") + + # Deep copy all complex objects + cloned.level = self.level + cloned.stats = copy.deepcopy(self.stats) + cloned.equipment = copy.deepcopy(self.equipment) + cloned.skills = copy.deepcopy(self.skills) + cloned.stealth_level = self.stealth_level + cloned.achievements = copy.deepcopy(self.achievements) + + return cloned + + def display(self): + print("🗡️ ROGUE CHARACTER") + print(f"├─ Name: {self.name}") + print(f"├─ Class: {self.character_class}") + print(f"├─ Level: {self.level}") + print(f"├─ Stats: {self.stats}") + print(f"├─ Equipment: {self.equipment}") + print(f"├─ Skills: {', '.join(self.skills)}") + print(f"├─ Stealth: {self.stealth_level}%") + print(f"└─ Achievements: {len(self.achievements)} unlocked") + + def get_type(self): + return "Rogue" + + def level_up(self): + self.level += 1 + self.stats.base_stats['agility'] += 2 + self.stats.base_stats['intelligence'] += 1 + self.stealth_level += 5 + self.stats.add_experience(self.level * 100, f"Level {self.level}") + + if self.level == 4: + self.skills.append("Dual Wield") + elif self.level == 8: + self.skills.append("Shadow Clone") + +class CharacterFactory: + """Factory for creating characters using prototypes""" + + def __init__(self): + self.prototypes = {} + self._initialize_prototypes() + + def _initialize_prototypes(self): + """Initialize default character prototypes""" + print("🏭 Initializing character prototypes...") + + # Create template characters + warrior_template = WarriorCharacter("Template Warrior") + warrior_template.level = 5 + warrior_template.level_up() # Add level 5 skill + warrior_template.equipment.add_to_inventory("Gold", 500) + warrior_template.achievements.append("Template Character") + + mage_template = MageCharacter("Template Mage") + mage_template.level = 3 + mage_template.level_up() # Add level 3 spell + mage_template.equipment.add_to_inventory("Gold", 300) + mage_template.achievements.append("Template Character") + + rogue_template = RogueCharacter("Template Rogue") + rogue_template.level = 4 + rogue_template.level_up() # Add level 4 skill + rogue_template.equipment.add_to_inventory("Gold", 400) + rogue_template.achievements.append("Template Character") + + # Register prototypes + self.prototypes['warrior'] = warrior_template + self.prototypes['mage'] = mage_template + self.prototypes['rogue'] = rogue_template + + print(f"✅ Prototypes initialized: {list(self.prototypes.keys())}") + + def create_character(self, character_type, name=None): + """Create a new character by cloning a prototype""" + prototype = self.prototypes.get(character_type.lower()) + if not prototype: + raise ValueError(f"Unknown character type: {character_type}") + + cloned_character = prototype.clone() + if name: + cloned_character.name = name + + return cloned_character + + def register_prototype(self, name, prototype): + """Register a new character prototype""" + self.prototypes[name] = prototype + print(f"📝 Registered new prototype: {name}") + + def get_available_types(self): + """Get list of available character types""" + return list(self.prototypes.keys()) + +def demonstrate_shallow_vs_deep_copy(): + """Demonstrate the difference between shallow and deep copying""" + print("\n--- Shallow vs Deep Copy Demonstration ---") + + original = WarriorCharacter("Original Warrior") + original.equipment.add_to_inventory("Magic Sword", 1) + original.stats.add_modifier("strength", 5) + + # Shallow copy (problematic) + shallow_copy = copy.copy(original) + shallow_copy.name = "Shallow Copy Warrior" + + # Deep copy (correct) + deep_copy = copy.deepcopy(original) + deep_copy.name = "Deep Copy Warrior" + + print("Original before modification:") + print(f" Equipment inventory: {original.equipment.inventory}") + print(f" Stats modifiers: {original.stats.modifiers}") + + # Modify shallow copy's complex objects + shallow_copy.equipment.add_to_inventory("Cursed Item", 1) + shallow_copy.stats.add_modifier("strength", -10) + + # Modify deep copy's complex objects + deep_copy.equipment.add_to_inventory("Blessed Item", 1) + deep_copy.stats.add_modifier("strength", 10) + + print("\nAfter modifications:") + print("Original:") + print(f" Equipment inventory: {original.equipment.inventory}") + print(f" Stats modifiers: {original.stats.modifiers}") + + print("Shallow Copy:") + print(f" Equipment inventory: {shallow_copy.equipment.inventory}") + print(f" Stats modifiers: {shallow_copy.stats.modifiers}") + + print("Deep Copy:") + print(f" Equipment inventory: {deep_copy.equipment.inventory}") + print(f" Stats modifiers: {deep_copy.stats.modifiers}") + + print("\n⚠️ Notice: Shallow copy modifications affected the original!") + print("✅ Deep copy modifications are independent of the original.") + +def main(): + print("=== Prototype Pattern Demo - Game Character System ===\n") + + factory = CharacterFactory() + + print("\n--- Creating Characters from Prototypes ---") + + # Create characters using prototypes + warrior = factory.create_character("warrior", "Conan the Barbarian") + mage = factory.create_character("mage", "Gandalf the Grey") + rogue = factory.create_character("rogue", "Robin Hood") + + print("\nDisplaying created characters:") + warrior.display() + print() + mage.display() + print() + rogue.display() + + print("\n--- Testing Independent Modification ---") + + # Create another warrior and modify it independently + warrior2 = factory.create_character("warrior", "Aragorn") + warrior2.level_up() + warrior2.level_up() + warrior2.equipment.add_weapon("Legendary Sword") + warrior2.equipment.add_to_inventory("Dragon Scale", 1) + warrior2.achievements.append("Dragon Slayer") + + print("Original warrior after creating and modifying warrior2:") + warrior.display() + print("\nModified warrior2:") + warrior2.display() + + print("\n✅ Independent modification verified!") + + # Demonstrate shallow vs deep copy + demonstrate_shallow_vs_deep_copy() + + print("\n--- Custom Prototype Registration ---") + + # Create a custom paladin character + paladin = WarriorCharacter("Template Paladin") + paladin.character_class = "Paladin" + paladin.stats.base_stats['intelligence'] = 14 # Higher than warrior + paladin.equipment.add_weapon("Holy Sword") + paladin.equipment.set_armor("chest", "Plate Mail") + paladin.skills.append("Divine Magic") + paladin.skills.append("Heal") + paladin.achievements.append("Holy Warrior") + + factory.register_prototype("paladin", paladin) + + # Create a paladin using the new prototype + custom_paladin = factory.create_character("paladin", "Sir Galahad") + print("Character created from custom prototype:") + custom_paladin.display() + + print("\n--- Performance Comparison ---") + + print(f"Available character types: {factory.get_available_types()}") + + # Measure cloning performance + start_time = time.time() + characters = [] + + for i in range(1000): + char_type = ['warrior', 'mage', 'rogue'][i % 3] + character = factory.create_character(char_type, f"Player{i}") + characters.append(character) + + end_time = time.time() + + print(f"📊 Created {len(characters)} characters in {end_time - start_time:.4f}s using prototypes") + print(f"⚡ Average time per character: {(end_time - start_time) / len(characters):.6f}s") + + print("\n✅ Prototype pattern successfully demonstrated!") + print("Benefits: Fast object creation, template-based instantiation, independent copies") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/proxy/solution.cpp b/src/content/design-pattern/code/proxy/solution.cpp new file mode 100644 index 0000000..db05cd5 --- /dev/null +++ b/src/content/design-pattern/code/proxy/solution.cpp @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Subject interface +class ImageViewer { +public: + virtual ~ImageViewer() = default; + virtual void displayImage() = 0; + virtual std::string getImageInfo() = 0; +}; + +// Real Subject - Heavy image that takes time to load +class HighResolutionImage : public ImageViewer { +private: + std::string filename; + std::string imageData; + long fileSize; + + void loadImageFromDisk() { + std::cout << "Loading high-resolution image: " << filename << std::endl; + std::cout << "File size: " << fileSize << " KB" << std::endl; + + // Simulate time-consuming loading process + std::this_thread::sleep_for(std::chrono::seconds(2)); + + this->imageData = "Raw image data for " + filename; + std::cout << "✓ Image loaded successfully!" << std::endl; + } + +public: + HighResolutionImage(const std::string& filename) : filename(filename) { + // Random file size + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(5000, 15000); + this->fileSize = dis(gen); + loadImageFromDisk(); // Expensive operation + } + + void displayImage() override { + std::cout << "🖼️ Displaying: " << filename << std::endl; + std::cout << " Resolution: 4K Ultra HD" << std::endl; + std::cout << " Size: " << fileSize << " KB" << std::endl; + } + + std::string getImageInfo() override { + return filename + " (" + std::to_string(fileSize) + " KB)"; + } +}; + +// Proxy - Controls access and provides additional functionality +class ImageProxy : public ImageViewer { +private: + std::unique_ptr realImage; + std::string filename; + std::string userRole; + static std::unordered_map> imageCache; + static std::unordered_set accessLog; + + HighResolutionImage* getRealImage() { + if (realImage == nullptr) { + // Check cache first + auto it = imageCache.find(filename); + if (it != imageCache.end()) { + std::cout << "📋 Loading from cache: " << filename << std::endl; + realImage = std::move(it->second); + imageCache.erase(it); + } else { + // Load from disk and cache it + realImage = std::make_unique(filename); + std::cout << "💾 Image cached for future use" << std::endl; + } + } + return realImage.get(); + } + + bool hasAccess() { + if (filename.find("confidential") != std::string::npos && userRole != "admin") { + return false; + } + if (filename.find("premium") != std::string::npos && userRole == "guest") { + return false; + } + return true; + } + + void logAccess() { + std::string logEntry = userRole + " accessed " + filename; + accessLog.insert(logEntry); + std::cout << "📝 Access logged: " << logEntry << std::endl; + } + +public: + ImageProxy(const std::string& filename, const std::string& userRole) + : filename(filename), userRole(userRole) {} + + void displayImage() override { + // Access control + if (!hasAccess()) { + std::cout << "❌ Access denied! User role '" << userRole + << "' cannot view: " << filename << std::endl; + return; + } + + // Logging + logAccess(); + + // Additional functionality before delegation + std::cout << "🔍 Proxy: Preparing to display " << filename << std::endl; + + // Lazy loading and delegation to real object + HighResolutionImage* image = getRealImage(); + image->displayImage(); + + // Additional functionality after delegation + std::cout << "📊 Proxy: Display completed, updating view statistics" << std::endl; + } + + std::string getImageInfo() override { + // Some info can be provided without loading the actual image + if (realImage == nullptr && imageCache.find(filename) == imageCache.end()) { + return filename + " (not loaded yet)"; + } else { + return getRealImage()->getImageInfo(); + } + } + + static void printAccessLog() { + std::cout << "\n=== ACCESS LOG ===" << std::endl; + for (const auto& entry : accessLog) { + std::cout << " " << entry << std::endl; + } + std::cout << "==================\n" << std::endl; + } + + static void printCacheStatus() { + std::cout << "=== CACHE STATUS ===" << std::endl; + std::cout << "Images in cache: " << imageCache.size() << std::endl; + for (const auto& pair : imageCache) { + std::cout << " - " << pair.first << std::endl; + } + std::cout << "===================\n" << std::endl; + } +}; + +// Static member definitions +std::unordered_map> ImageProxy::imageCache; +std::unordered_set ImageProxy::accessLog; + +int main() { + std::cout << "=== PROXY PATTERN DEMO ===\n" << std::endl; + + // Create image proxies for different users + std::vector> images; + images.push_back(std::make_unique("nature_landscape.jpg", "user")); + images.push_back(std::make_unique("confidential_document.jpg", "user")); + images.push_back(std::make_unique("premium_photo.jpg", "guest")); + images.push_back(std::make_unique("vacation_photo.jpg", "admin")); + images.push_back(std::make_unique("confidential_blueprint.jpg", "admin")); + + std::cout << "1. INITIAL ACCESS - Images not loaded yet" << std::endl; + std::cout << "Getting image info (lightweight operation):" << std::endl; + for (const auto& image : images) { + std::cout << " - " << image->getImageInfo() << std::endl; + } + + std::cout << "\n2. FIRST DISPLAY ATTEMPTS" << std::endl; + std::cout << "Now attempting to display images (heavy operation):\n" << std::endl; + + for (const auto& image : images) { + std::cout << "--- Attempting to display ---" << std::endl; + image->displayImage(); + std::cout << std::endl; + } + + std::cout << "3. SECOND ACCESS - Should use cache" << std::endl; + std::cout << "Displaying the first image again (should be faster):\n" << std::endl; + images[0]->displayImage(); + + std::cout << "\n4. PROXY FEATURES DEMONSTRATION" << std::endl; + ImageProxy::printAccessLog(); + ImageProxy::printCacheStatus(); + + std::cout << "=== PROXY BENEFITS ===" << std::endl; + std::cout << "✓ Lazy Loading: Images only loaded when displayed" << std::endl; + std::cout << "✓ Caching: Subsequent access is faster" << std::endl; + std::cout << "✓ Access Control: Role-based permissions enforced" << std::endl; + std::cout << "✓ Logging: All access attempts are logged" << std::endl; + std::cout << "✓ Transparent: Client code doesn't know about proxy" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/proxy/solution.java b/src/content/design-pattern/code/proxy/solution.java new file mode 100644 index 0000000..4e89b08 --- /dev/null +++ b/src/content/design-pattern/code/proxy/solution.java @@ -0,0 +1,190 @@ +import java.util.*; + +// Subject interface +interface ImageViewer { + void displayImage(); + String getImageInfo(); +} + +// Real Subject - Heavy image that takes time to load +class HighResolutionImage implements ImageViewer { + private String filename; + private String imageData; + private long fileSize; + + public HighResolutionImage(String filename) { + this.filename = filename; + this.fileSize = (long)(Math.random() * 10000 + 5000); // Random file size + loadImageFromDisk(); // Expensive operation + } + + private void loadImageFromDisk() { + System.out.println("Loading high-resolution image: " + filename); + System.out.println("File size: " + fileSize + " KB"); + + // Simulate time-consuming loading process + try { + Thread.sleep(2000); // Simulate 2 seconds loading time + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + this.imageData = "Raw image data for " + filename; + System.out.println("✓ Image loaded successfully!"); + } + + @Override + public void displayImage() { + System.out.println("🖼️ Displaying: " + filename); + System.out.println(" Resolution: 4K Ultra HD"); + System.out.println(" Size: " + fileSize + " KB"); + } + + @Override + public String getImageInfo() { + return filename + " (" + fileSize + " KB)"; + } +} + +// Proxy - Controls access and provides additional functionality +class ImageProxy implements ImageViewer { + private HighResolutionImage realImage; + private String filename; + private String userRole; + private static Map imageCache = new HashMap<>(); + private static Set accessLog = new HashSet<>(); + + public ImageProxy(String filename, String userRole) { + this.filename = filename; + this.userRole = userRole; + } + + // Lazy loading - only load image when needed + private HighResolutionImage getRealImage() { + if (realImage == null) { + // Check cache first + if (imageCache.containsKey(filename)) { + System.out.println("📋 Loading from cache: " + filename); + realImage = imageCache.get(filename); + } else { + // Load from disk and cache it + realImage = new HighResolutionImage(filename); + imageCache.put(filename, realImage); + System.out.println("💾 Image cached for future use"); + } + } + return realImage; + } + + @Override + public void displayImage() { + // Access control + if (!hasAccess()) { + System.out.println("❌ Access denied! User role '" + userRole + "' cannot view: " + filename); + return; + } + + // Logging + logAccess(); + + // Additional functionality before delegation + System.out.println("🔍 Proxy: Preparing to display " + filename); + + // Lazy loading and delegation to real object + HighResolutionImage image = getRealImage(); + image.displayImage(); + + // Additional functionality after delegation + System.out.println("📊 Proxy: Display completed, updating view statistics"); + } + + @Override + public String getImageInfo() { + // Some info can be provided without loading the actual image + if (realImage == null && !imageCache.containsKey(filename)) { + return filename + " (not loaded yet)"; + } else { + return getRealImage().getImageInfo(); + } + } + + private boolean hasAccess() { + // Simple role-based access control + if (filename.contains("confidential") && !userRole.equals("admin")) { + return false; + } + if (filename.contains("premium") && userRole.equals("guest")) { + return false; + } + return true; + } + + private void logAccess() { + String logEntry = userRole + " accessed " + filename; + accessLog.add(logEntry); + System.out.println("📝 Access logged: " + logEntry); + } + + public static void printAccessLog() { + System.out.println("\n=== ACCESS LOG ==="); + for (String entry : accessLog) { + System.out.println(" " + entry); + } + System.out.println("==================\n"); + } + + public static void printCacheStatus() { + System.out.println("=== CACHE STATUS ==="); + System.out.println("Images in cache: " + imageCache.size()); + for (String filename : imageCache.keySet()) { + System.out.println(" - " + filename); + } + System.out.println("===================\n"); + } +} + +// Client code +public class ProxyPatternDemo { + public static void main(String[] args) { + System.out.println("=== PROXY PATTERN DEMO ===\n"); + + // Create image proxies for different users + ImageViewer[] images = { + new ImageProxy("nature_landscape.jpg", "user"), + new ImageProxy("confidential_document.jpg", "user"), + new ImageProxy("premium_photo.jpg", "guest"), + new ImageProxy("vacation_photo.jpg", "admin"), + new ImageProxy("confidential_blueprint.jpg", "admin") + }; + + System.out.println("1. INITIAL ACCESS - Images not loaded yet"); + System.out.println("Getting image info (lightweight operation):"); + for (ImageViewer image : images) { + System.out.println(" - " + image.getImageInfo()); + } + + System.out.println("\n2. FIRST DISPLAY ATTEMPTS"); + System.out.println("Now attempting to display images (heavy operation):\n"); + + for (ImageViewer image : images) { + System.out.println("--- Attempting to display ---"); + image.displayImage(); + System.out.println(); + } + + System.out.println("3. SECOND ACCESS - Should use cache"); + System.out.println("Displaying the first image again (should be faster):\n"); + images[0].displayImage(); + + System.out.println("\n4. PROXY FEATURES DEMONSTRATION"); + ImageProxy.printAccessLog(); + ImageProxy.printCacheStatus(); + + System.out.println("=== PROXY BENEFITS ==="); + System.out.println("✓ Lazy Loading: Images only loaded when displayed"); + System.out.println("✓ Caching: Subsequent access is faster"); + System.out.println("✓ Access Control: Role-based permissions enforced"); + System.out.println("✓ Logging: All access attempts are logged"); + System.out.println("✓ Transparent: Client code doesn't know about proxy"); + } +} diff --git a/src/content/design-pattern/code/proxy/solution.py b/src/content/design-pattern/code/proxy/solution.py new file mode 100644 index 0000000..8140ad9 --- /dev/null +++ b/src/content/design-pattern/code/proxy/solution.py @@ -0,0 +1,159 @@ +import time +from abc import ABC, abstractmethod +from typing import Dict, Set, Optional + +# Subject interface +class ImageViewer(ABC): + @abstractmethod + def display_image(self) -> None: + pass + + @abstractmethod + def get_image_info(self) -> str: + pass + +# Real Subject - Heavy image that takes time to load +class HighResolutionImage(ImageViewer): + def __init__(self, filename: str): + self.filename = filename + self.file_size = int(__import__('random').randint(5000, 15000)) # Random file size + self.image_data = None + self._load_image_from_disk() # Expensive operation + + def _load_image_from_disk(self) -> None: + print(f"Loading high-resolution image: {self.filename}") + print(f"File size: {self.file_size} KB") + + # Simulate time-consuming loading process + time.sleep(2) # Simulate 2 seconds loading time + + self.image_data = f"Raw image data for {self.filename}" + print("✓ Image loaded successfully!") + + def display_image(self) -> None: + print(f"🖼️ Displaying: {self.filename}") + print(f" Resolution: 4K Ultra HD") + print(f" Size: {self.file_size} KB") + + def get_image_info(self) -> str: + return f"{self.filename} ({self.file_size} KB)" + +# Proxy - Controls access and provides additional functionality +class ImageProxy(ImageViewer): + _image_cache: Dict[str, HighResolutionImage] = {} + _access_log: Set[str] = set() + + def __init__(self, filename: str, user_role: str): + self.filename = filename + self.user_role = user_role + self.real_image: Optional[HighResolutionImage] = None + + def _get_real_image(self) -> HighResolutionImage: + if self.real_image is None: + # Check cache first + if self.filename in ImageProxy._image_cache: + print(f"📋 Loading from cache: {self.filename}") + self.real_image = ImageProxy._image_cache[self.filename] + else: + # Load from disk and cache it + self.real_image = HighResolutionImage(self.filename) + ImageProxy._image_cache[self.filename] = self.real_image + print("💾 Image cached for future use") + return self.real_image + + def display_image(self) -> None: + # Access control + if not self._has_access(): + print(f"❌ Access denied! User role '{self.user_role}' cannot view: {self.filename}") + return + + # Logging + self._log_access() + + # Additional functionality before delegation + print(f"🔍 Proxy: Preparing to display {self.filename}") + + # Lazy loading and delegation to real object + image = self._get_real_image() + image.display_image() + + # Additional functionality after delegation + print("📊 Proxy: Display completed, updating view statistics") + + def get_image_info(self) -> str: + # Some info can be provided without loading the actual image + if self.real_image is None and self.filename not in ImageProxy._image_cache: + return f"{self.filename} (not loaded yet)" + else: + return self._get_real_image().get_image_info() + + def _has_access(self) -> bool: + # Simple role-based access control + if "confidential" in self.filename and self.user_role != "admin": + return False + if "premium" in self.filename and self.user_role == "guest": + return False + return True + + def _log_access(self) -> None: + log_entry = f"{self.user_role} accessed {self.filename}" + ImageProxy._access_log.add(log_entry) + print(f"📝 Access logged: {log_entry}") + + @classmethod + def print_access_log(cls) -> None: + print("\n=== ACCESS LOG ===") + for entry in cls._access_log: + print(f" {entry}") + print("==================\n") + + @classmethod + def print_cache_status(cls) -> None: + print("=== CACHE STATUS ===") + print(f"Images in cache: {len(cls._image_cache)}") + for filename in cls._image_cache.keys(): + print(f" - {filename}") + print("===================\n") + +def main(): + print("=== PROXY PATTERN DEMO ===\n") + + # Create image proxies for different users + images = [ + ImageProxy("nature_landscape.jpg", "user"), + ImageProxy("confidential_document.jpg", "user"), + ImageProxy("premium_photo.jpg", "guest"), + ImageProxy("vacation_photo.jpg", "admin"), + ImageProxy("confidential_blueprint.jpg", "admin") + ] + + print("1. INITIAL ACCESS - Images not loaded yet") + print("Getting image info (lightweight operation):") + for image in images: + print(f" - {image.get_image_info()}") + + print("\n2. FIRST DISPLAY ATTEMPTS") + print("Now attempting to display images (heavy operation):\n") + + for image in images: + print("--- Attempting to display ---") + image.display_image() + print() + + print("3. SECOND ACCESS - Should use cache") + print("Displaying the first image again (should be faster):\n") + images[0].display_image() + + print("\n4. PROXY FEATURES DEMONSTRATION") + ImageProxy.print_access_log() + ImageProxy.print_cache_status() + + print("=== PROXY BENEFITS ===") + print("✓ Lazy Loading: Images only loaded when displayed") + print("✓ Caching: Subsequent access is faster") + print("✓ Access Control: Role-based permissions enforced") + print("✓ Logging: All access attempts are logged") + print("✓ Transparent: Client code doesn't know about proxy") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/singleton/solution.cpp b/src/content/design-pattern/code/singleton/solution.cpp new file mode 100644 index 0000000..00c02a6 --- /dev/null +++ b/src/content/design-pattern/code/singleton/solution.cpp @@ -0,0 +1,535 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Singleton Pattern - Resource Manager System + * Thread-safe singleton managing system resources with lazy initialization + */ + +class ResourceManager { +private: + // Static instance pointer and mutex for thread safety + static std::unique_ptr instance; + static std::mutex instanceMutex; + + // Instance data and synchronization + mutable std::mutex dataMutex; + std::map resources; + std::vector loadedResources; + std::atomic accessCount{0}; + std::string configPath; + bool initialized; + + // Private constructor prevents direct instantiation + ResourceManager() : configPath("resources.conf"), initialized(false) { + std::cout << "🔧 ResourceManager constructor called on thread: " + << std::this_thread::get_id() << std::endl; + loadDefaultResources(); + initialized = true; + } + + void loadDefaultResources() { + std::lock_guard lock(dataMutex); + + // Load default system resources + resources["system.name"] = "MyApplication"; + resources["system.version"] = "1.0.0"; + resources["system.environment"] = "development"; + + // Database resources + resources["database.host"] = "localhost"; + resources["database.port"] = "5432"; + resources["database.name"] = "myapp"; + resources["database.pool_size"] = "10"; + + // Cache resources + resources["cache.enabled"] = "true"; + resources["cache.ttl"] = "3600"; + resources["cache.max_entries"] = "1000"; + + // Logging resources + resources["logging.level"] = "INFO"; + resources["logging.file"] = "application.log"; + resources["logging.console"] = "true"; + + loadedResources.push_back("Default Resources"); + + std::cout << "📚 Loaded " << resources.size() << " default resources" << std::endl; + } + +public: + // Deleted copy constructor and assignment operator to prevent copying + ResourceManager(const ResourceManager&) = delete; + ResourceManager& operator=(const ResourceManager&) = delete; + + // Thread-safe singleton instance retrieval using Meyer's Singleton (C++11 guarantees) + static ResourceManager& getInstance() { + // C++11 guarantees that local static initialization is thread-safe + static ResourceManager instance; + return instance; + } + + // Alternative implementation with explicit double-checked locking + static ResourceManager& getInstanceExplicit() { + // First check without lock (performance optimization) + if (instance == nullptr) { + std::lock_guard lock(instanceMutex); + // Second check with lock (thread safety) + if (instance == nullptr) { + instance = std::unique_ptr(new ResourceManager()); + } + } + return *instance; + } + + // Resource access methods + std::string getResource(const std::string& key, const std::string& defaultValue = "") const { + std::lock_guard lock(dataMutex); + accessCount++; + + auto it = resources.find(key); + if (it != resources.end()) { + return it->second; + } + return defaultValue; + } + + void setResource(const std::string& key, const std::string& value) { + std::lock_guard lock(dataMutex); + + std::string oldValue = resources[key]; + resources[key] = value; + + std::cout << "🔄 Resource updated: " << key << " = '" << value + << "' (was: '" << oldValue << "')" << std::endl; + } + + bool hasResource(const std::string& key) const { + std::lock_guard lock(dataMutex); + return resources.find(key) != resources.end(); + } + + void removeResource(const std::string& key) { + std::lock_guard lock(dataMutex); + + auto it = resources.find(key); + if (it != resources.end()) { + std::cout << "🗑️ Removed resource: " << key << " (was: '" << it->second << "')" << std::endl; + resources.erase(it); + } + } + + // Configuration file operations + bool loadFromFile(const std::string& filename) { + std::ifstream file(filename); + if (!file.is_open()) { + std::cout << "⚠️ Could not open file: " << filename << std::endl; + return false; + } + + std::lock_guard lock(dataMutex); + std::string line; + int loadedCount = 0; + + while (std::getline(file, line)) { + // Skip empty lines and comments + if (line.empty() || line[0] == '#') continue; + + size_t pos = line.find('='); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + + // Trim whitespace + key.erase(0, key.find_first_not_of(" \t")); + key.erase(key.find_last_not_of(" \t") + 1); + value.erase(0, value.find_first_not_of(" \t")); + value.erase(value.find_last_not_of(" \t") + 1); + + resources[key] = value; + loadedCount++; + } + } + + configPath = filename; + loadedResources.push_back("File: " + filename + " (" + std::to_string(loadedCount) + " resources)"); + + std::cout << "📁 Loaded " << loadedCount << " resources from " << filename << std::endl; + return true; + } + + bool saveToFile(const std::string& filename) const { + std::ofstream file(filename); + if (!file.is_open()) { + std::cout << "❌ Could not create file: " << filename << std::endl; + return false; + } + + std::lock_guard lock(dataMutex); + + file << "# Resource configuration file" << std::endl; + file << "# Generated at: " << getCurrentTimeString() << std::endl; + file << std::endl; + + for (const auto& pair : resources) { + file << pair.first << "=" << pair.second << std::endl; + } + + std::cout << "💾 Saved " << resources.size() << " resources to " << filename << std::endl; + return true; + } + + // Resource categories + std::map getResourcesByPrefix(const std::string& prefix) const { + std::lock_guard lock(dataMutex); + std::map result; + + for (const auto& pair : resources) { + if (pair.first.substr(0, prefix.length()) == prefix) { + result[pair.first] = pair.second; + } + } + + return result; + } + + // Statistics and information + struct Statistics { + size_t totalResources; + int accessCount; + size_t loadedSources; + std::string configPath; + std::thread::id threadId; + bool initialized; + }; + + Statistics getStatistics() const { + std::lock_guard lock(dataMutex); + + return { + resources.size(), + accessCount.load(), + loadedResources.size(), + configPath, + std::this_thread::get_id(), + initialized + }; + } + + void printConfiguration() const { + std::lock_guard lock(dataMutex); + + std::cout << "📋 Resource Manager Configuration:" << std::endl; + std::cout << "├─ Total Resources: " << resources.size() << std::endl; + std::cout << "├─ Access Count: " << accessCount.load() << std::endl; + std::cout << "├─ Config File: " << configPath << std::endl; + std::cout << "├─ Thread ID: " << std::this_thread::get_id() << std::endl; + std::cout << "└─ Initialized: " << (initialized ? "Yes" : "No") << std::endl; + + std::cout << "\n📚 Loaded Sources:" << std::endl; + for (const auto& source : loadedResources) { + std::cout << " • " << source << std::endl; + } + + std::cout << "\n🔧 Current Resources:" << std::endl; + for (const auto& pair : resources) { + std::cout << " " << pair.first << " = " << pair.second << std::endl; + } + } + + void clearResources() { + std::lock_guard lock(dataMutex); + + size_t count = resources.size(); + resources.clear(); + loadedResources.clear(); + + std::cout << "🧹 Cleared " << count << " resources" << std::endl; + + // Reload defaults + loadDefaultResources(); + } + +private: + std::string getCurrentTimeString() const { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::stringstream ss; + ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S"); + return ss.str(); + } +}; + +// Static member definitions +std::unique_ptr ResourceManager::instance = nullptr; +std::mutex ResourceManager::instanceMutex; + +// Example classes demonstrating resource usage +class DatabaseConnection { +private: + ResourceManager& resourceManager; + std::string connectionString; + bool connected; + +public: + DatabaseConnection() : resourceManager(ResourceManager::getInstance()), connected(false) { + std::cout << "🗄️ DatabaseConnection created" << std::endl; + } + + void connect() { + std::string host = resourceManager.getResource("database.host", "localhost"); + std::string port = resourceManager.getResource("database.port", "5432"); + std::string dbName = resourceManager.getResource("database.name", "default"); + std::string poolSize = resourceManager.getResource("database.pool_size", "5"); + + connectionString = "postgresql://" + host + ":" + port + "/" + dbName; + + std::cout << "🔌 Connecting to database..." << std::endl; + std::cout << " Host: " << host << ":" << port << std::endl; + std::cout << " Database: " << dbName << std::endl; + std::cout << " Pool size: " << poolSize << std::endl; + + // Simulate connection delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + connected = true; + std::cout << "✅ Connected to database successfully" << std::endl; + } + + void disconnect() { + if (connected) { + connected = false; + std::cout << "🔌 Disconnected from database" << std::endl; + } + } + + std::string getConnectionString() const { + return connectionString; + } + + bool isConnected() const { + return connected; + } +}; + +class CacheManager { +private: + ResourceManager& resourceManager; + std::map cache; + size_t maxEntries; + int ttl; + bool enabled; + +public: + CacheManager() : resourceManager(ResourceManager::getInstance()) { + initialize(); + } + + void initialize() { + enabled = resourceManager.getResource("cache.enabled", "true") == "true"; + ttl = std::stoi(resourceManager.getResource("cache.ttl", "3600")); + maxEntries = std::stoull(resourceManager.getResource("cache.max_entries", "1000")); + + std::cout << "🧠 Cache Manager initialized:" << std::endl; + std::cout << " Enabled: " << (enabled ? "Yes" : "No") << std::endl; + std::cout << " TTL: " << ttl << " seconds" << std::endl; + std::cout << " Max entries: " << maxEntries << std::endl; + } + + void put(const std::string& key, const std::string& value) { + if (!enabled) return; + + if (cache.size() >= maxEntries) { + cache.clear(); // Simple eviction strategy + std::cout << "🗑️ Cache cleared due to size limit" << std::endl; + } + + cache[key] = value; + std::cout << "📝 Cached: " << key << " = " << value << std::endl; + } + + std::string get(const std::string& key) { + if (!enabled) return ""; + + auto it = cache.find(key); + if (it != cache.end()) { + std::cout << "🎯 Cache hit: " << key << std::endl; + return it->second; + } + + std::cout << "❌ Cache miss: " << key << std::endl; + return ""; + } + + void printStatus() const { + std::cout << "🧠 Cache Status:" << std::endl; + std::cout << " Enabled: " << (enabled ? "Yes" : "No") << std::endl; + std::cout << " Entries: " << cache.size() << "/" << maxEntries << std::endl; + std::cout << " TTL: " << ttl << "s" << std::endl; + } +}; + +// Thread worker function for testing thread safety +void resourceWorker(int workerId, int operationCount) { + ResourceManager& rm = ResourceManager::getInstance(); + + std::cout << "🏃 Worker " << workerId << " started on thread " + << std::this_thread::get_id() << std::endl; + + for (int i = 0; i < operationCount; ++i) { + // Read operations + std::string systemName = rm.getResource("system.name"); + std::string dbHost = rm.getResource("database.host"); + + // Write operations + rm.setResource("worker" + std::to_string(workerId) + ".counter", std::to_string(i)); + rm.setResource("worker" + std::to_string(workerId) + ".thread", + std::to_string(std::hash{}(std::this_thread::get_id()))); + + // Small delay to allow thread interleaving + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "✅ Worker " << workerId << " completed " << operationCount << " operations" << std::endl; +} + +int main() { + std::cout << "=== Singleton Pattern Demo - Resource Manager ===\n" << std::endl; + + // Demonstrate singleton behavior + std::cout << "1. Demonstrating Singleton Behavior:" << std::endl; + ResourceManager& rm1 = ResourceManager::getInstance(); + ResourceManager& rm2 = ResourceManager::getInstance(); + + std::cout << "rm1 == rm2: " << (&rm1 == &rm2 ? "true" : "false") << std::endl; + std::cout << "rm1 address: " << &rm1 << std::endl; + std::cout << "rm2 address: " << &rm2 << std::endl; + + // Basic resource operations + std::cout << "\n2. Basic Resource Operations:" << std::endl; + + std::cout << "System name: " << rm1.getResource("system.name") << std::endl; + std::cout << "Database host: " << rm1.getResource("database.host") << std::endl; + std::cout << "Non-existent resource: '" << rm1.getResource("non.existent", "default") << "'" << std::endl; + + // Set new resources + rm1.setResource("system.version", "2.0.0"); + rm1.setResource("feature.newfeature", "enabled"); + + std::cout << "Updated system version: " << rm1.getResource("system.version") << std::endl; + + // Configuration sections + std::cout << "\n3. Configuration Sections:" << std::endl; + + std::cout << "Database configuration:" << std::endl; + auto dbConfig = rm1.getResourcesByPrefix("database."); + for (const auto& pair : dbConfig) { + std::cout << " " << pair.first << " = " << pair.second << std::endl; + } + + std::cout << "\nCache configuration:" << std::endl; + auto cacheConfig = rm1.getResourcesByPrefix("cache."); + for (const auto& pair : cacheConfig) { + std::cout << " " << pair.first << " = " << pair.second << std::endl; + } + + // Services using the singleton + std::cout << "\n4. Services Using Resource Manager:" << std::endl; + + DatabaseConnection dbConn; + dbConn.connect(); + std::cout << "Connection string: " << dbConn.getConnectionString() << std::endl; + + CacheManager cacheManager; + cacheManager.put("user:123", "John Doe"); + cacheManager.put("user:456", "Jane Smith"); + std::string user = cacheManager.get("user:123"); + cacheManager.printStatus(); + + // File operations + std::cout << "\n5. File Operations:" << std::endl; + + // Save current configuration + rm1.saveToFile("demo_resources.conf"); + + // Modify configuration + rm1.setResource("temp.value", "temporary"); + std::cout << "Temp value before reload: " << rm1.getResource("temp.value") << std::endl; + + // Reload from file (temp.value should be gone) + rm1.loadFromFile("demo_resources.conf"); + std::cout << "Temp value after reload: '" << rm1.getResource("temp.value", "not found") << "'" << std::endl; + + // Thread safety demonstration + std::cout << "\n6. Thread Safety Demonstration:" << std::endl; + + const int threadCount = 5; + const int operationsPerThread = 5; + std::vector threads; + + auto startTime = std::chrono::high_resolution_clock::now(); + + // Create and start threads + for (int i = 0; i < threadCount; ++i) { + threads.emplace_back(resourceWorker, i, operationsPerThread); + } + + // Wait for all threads to complete + for (auto& thread : threads) { + thread.join(); + } + + auto endTime = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(endTime - startTime); + + std::cout << "⏱️ Thread test completed in " << duration.count() << "ms" << std::endl; + + // Statistics + std::cout << "\n7. Resource Manager Statistics:" << std::endl; + + auto stats = rm1.getStatistics(); + std::cout << "Total resources: " << stats.totalResources << std::endl; + std::cout << "Access count: " << stats.accessCount << std::endl; + std::cout << "Loaded sources: " << stats.loadedSources << std::endl; + std::cout << "Current thread: " << stats.threadId << std::endl; + std::cout << "Initialized: " << (stats.initialized ? "Yes" : "No") << std::endl; + + // Print full configuration + std::cout << "\n8. Full Configuration:" << std::endl; + rm1.printConfiguration(); + + // Test resource removal + std::cout << "\n9. Resource Management:" << std::endl; + rm1.removeResource("temp.removed"); + rm1.setResource("temp.test", "test_value"); + std::cout << "Has temp.test: " << (rm1.hasResource("temp.test") ? "Yes" : "No") << std::endl; + rm1.removeResource("temp.test"); + std::cout << "Has temp.test after removal: " << (rm1.hasResource("temp.test") ? "Yes" : "No") << std::endl; + + // Cleanup demonstration + std::cout << "\n10. Cleanup Operations:" << std::endl; + std::cout << "Resources before clear: " << stats.totalResources << std::endl; + rm1.clearResources(); + auto finalStats = rm1.getStatistics(); + std::cout << "Resources after clear: " << finalStats.totalResources << std::endl; + + // Cleanup services + dbConn.disconnect(); + + std::cout << "\n✅ Singleton pattern successfully demonstrated!" << std::endl; + std::cout << "Benefits: Single instance, thread safety, global resource access, lazy initialization" << std::endl; + + std::cout << "\nFinal access count: " << rm1.getStatistics().accessCount << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/singleton/solution.java b/src/content/design-pattern/code/singleton/solution.java new file mode 100644 index 0000000..a84517c --- /dev/null +++ b/src/content/design-pattern/code/singleton/solution.java @@ -0,0 +1,461 @@ +/** + * Singleton Pattern - Application Logger System + * Ensures only one instance of the logger exists throughout the application with thread-safety + */ + +import java.util.*; +import java.util.concurrent.*; +import java.io.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +// Thread-safe Singleton Logger implementation +class Logger { + // Volatile ensures that changes to the instance are visible across threads + private static volatile Logger instance; + + // Configuration and state + private final List logHistory; + private LogLevel currentLevel; + private boolean enableConsoleOutput; + private boolean enableFileOutput; + private String logFileName; + private final Object lockObject; // For synchronizing log operations + + public enum LogLevel { + DEBUG(0, "DEBUG", "🐛"), + INFO(1, "INFO", "ℹ️"), + WARN(2, "WARN", "⚠️"), + ERROR(3, "ERROR", "❌"), + FATAL(4, "FATAL", "💀"); + + private final int priority; + private final String name; + private final String emoji; + + LogLevel(int priority, String name, String emoji) { + this.priority = priority; + this.name = name; + this.emoji = emoji; + } + + public int getPriority() { return priority; } + public String getName() { return name; } + public String getEmoji() { return emoji; } + } + + // Private constructor prevents direct instantiation + private Logger() { + this.logHistory = new ArrayList<>(); + this.currentLevel = LogLevel.INFO; + this.enableConsoleOutput = true; + this.enableFileOutput = false; + this.logFileName = "application.log"; + this.lockObject = new Object(); + + // Log the logger initialization + logMessage(LogLevel.INFO, "Logger", "Logger initialized with default configuration"); + } + + // Thread-safe lazy initialization using double-checked locking + public static Logger getInstance() { + // First check without synchronization for performance + if (instance == null) { + synchronized (Logger.class) { + // Second check with synchronization + if (instance == null) { + instance = new Logger(); + } + } + } + return instance; + } + + // Core logging method with thread safety + private void logMessage(LogLevel level, String className, String message) { + if (level.getPriority() < currentLevel.getPriority()) { + return; // Skip if below current log level + } + + synchronized (lockObject) { + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + String threadName = Thread.currentThread().getName(); + String formattedMessage = String.format("[%s] [%s] [%s] %s %s: %s", + timestamp, threadName, level.getName(), level.getEmoji(), className, message); + + // Add to history + logHistory.add(formattedMessage); + + // Console output + if (enableConsoleOutput) { + System.out.println(formattedMessage); + } + + // File output + if (enableFileOutput) { + writeToFile(formattedMessage); + } + } + } + + // Public logging methods + public void debug(String className, String message) { + logMessage(LogLevel.DEBUG, className, message); + } + + public void info(String className, String message) { + logMessage(LogLevel.INFO, className, message); + } + + public void warn(String className, String message) { + logMessage(LogLevel.WARN, className, message); + } + + public void error(String className, String message) { + logMessage(LogLevel.ERROR, className, message); + } + + public void fatal(String className, String message) { + logMessage(LogLevel.FATAL, className, message); + } + + // Convenience methods with automatic class detection + public void info(String message) { + StackTraceElement caller = Thread.currentThread().getStackTrace()[2]; + info(caller.getClassName(), message); + } + + public void error(String message) { + StackTraceElement caller = Thread.currentThread().getStackTrace()[2]; + error(caller.getClassName(), message); + } + + public void warn(String message) { + StackTraceElement caller = Thread.currentThread().getStackTrace()[2]; + warn(caller.getClassName(), message); + } + + // Configuration methods + public void setLogLevel(LogLevel level) { + synchronized (lockObject) { + LogLevel oldLevel = this.currentLevel; + this.currentLevel = level; + info("Logger", "Log level changed from " + oldLevel.getName() + " to " + level.getName()); + } + } + + public void enableConsoleOutput(boolean enable) { + synchronized (lockObject) { + this.enableConsoleOutput = enable; + if (enable) { + info("Logger", "Console output enabled"); + } + } + } + + public void enableFileOutput(boolean enable) { + enableFileOutput(enable, logFileName); + } + + public void enableFileOutput(boolean enable, String fileName) { + synchronized (lockObject) { + this.enableFileOutput = enable; + if (fileName != null && !fileName.isEmpty()) { + this.logFileName = fileName; + } + if (enable) { + info("Logger", "File output enabled: " + this.logFileName); + } else { + info("Logger", "File output disabled"); + } + } + } + + // File writing helper + private void writeToFile(String message) { + try (FileWriter writer = new FileWriter(logFileName, true); + BufferedWriter bufferedWriter = new BufferedWriter(writer)) { + bufferedWriter.write(message); + bufferedWriter.newLine(); + } catch (IOException e) { + System.err.println("Failed to write to log file: " + e.getMessage()); + } + } + + // Statistics and information methods + public int getLogCount() { + synchronized (lockObject) { + return logHistory.size(); + } + } + + public List getRecentLogs(int count) { + synchronized (lockObject) { + int size = logHistory.size(); + int startIndex = Math.max(0, size - count); + return new ArrayList<>(logHistory.subList(startIndex, size)); + } + } + + public void clearLogs() { + synchronized (lockObject) { + int previousCount = logHistory.size(); + logHistory.clear(); + info("Logger", "Cleared " + previousCount + " log entries"); + } + } + + public String getConfiguration() { + synchronized (lockObject) { + return String.format(""" + Logger Configuration: + ├─ Current Level: %s (%s) + ├─ Console Output: %s + ├─ File Output: %s + ├─ Log File: %s + ├─ Total Logs: %d + └─ Thread ID: %s + """, + currentLevel.getName(), currentLevel.getEmoji(), + enableConsoleOutput ? "Enabled" : "Disabled", + enableFileOutput ? "Enabled" : "Disabled", + logFileName, + logHistory.size(), + Thread.currentThread().getName() + ); + } + } + + // Prevent cloning + @Override + protected Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException("Cannot clone Singleton Logger instance"); + } +} + +// Example classes to demonstrate logging usage +class DatabaseConnection { + private String connectionString; + private boolean connected; + + public DatabaseConnection(String connectionString) { + this.connectionString = connectionString; + this.connected = false; + Logger.getInstance().info("DatabaseConnection", "Created new database connection: " + connectionString); + } + + public void connect() { + Logger logger = Logger.getInstance(); + try { + logger.info("DatabaseConnection", "Attempting to connect to database..."); + + // Simulate connection process + Thread.sleep(100); + + if (connectionString.contains("invalid")) { + throw new RuntimeException("Invalid connection string"); + } + + connected = true; + logger.info("DatabaseConnection", "Successfully connected to database"); + + } catch (InterruptedException e) { + logger.error("DatabaseConnection", "Connection interrupted: " + e.getMessage()); + } catch (RuntimeException e) { + logger.error("DatabaseConnection", "Connection failed: " + e.getMessage()); + throw e; + } + } + + public void disconnect() { + Logger logger = Logger.getInstance(); + if (connected) { + connected = false; + logger.info("DatabaseConnection", "Disconnected from database"); + } else { + logger.warn("DatabaseConnection", "Attempted to disconnect but connection was not active"); + } + } + + public void executeQuery(String query) { + Logger logger = Logger.getInstance(); + if (!connected) { + logger.error("DatabaseConnection", "Cannot execute query - not connected to database"); + return; + } + + logger.debug("DatabaseConnection", "Executing query: " + query); + logger.info("DatabaseConnection", "Query executed successfully"); + } +} + +class UserService { + public void createUser(String username, String email) { + Logger logger = Logger.getInstance(); + logger.info("UserService", "Creating new user: " + username); + + if (username == null || username.isEmpty()) { + logger.error("UserService", "Username cannot be null or empty"); + throw new IllegalArgumentException("Username cannot be null or empty"); + } + + if (!email.contains("@")) { + logger.warn("UserService", "Email format appears invalid: " + email); + } + + logger.debug("UserService", "Validating user data..."); + logger.info("UserService", "User created successfully: " + username); + } + + public void deleteUser(String username) { + Logger logger = Logger.getInstance(); + logger.warn("UserService", "Deleting user: " + username); + logger.info("UserService", "User deleted: " + username); + } +} + +// Thread test to demonstrate singleton behavior +class LoggingWorker implements Runnable { + private final String workerName; + private final int messageCount; + + public LoggingWorker(String workerName, int messageCount) { + this.workerName = workerName; + this.messageCount = messageCount; + } + + @Override + public void run() { + Logger logger = Logger.getInstance(); + + for (int i = 1; i <= messageCount; i++) { + logger.info("LoggingWorker", workerName + " - Message " + i); + + try { + Thread.sleep(10); // Small delay to interleave messages + } catch (InterruptedException e) { + logger.error("LoggingWorker", workerName + " interrupted"); + break; + } + } + + logger.info("LoggingWorker", workerName + " completed " + messageCount + " messages"); + } +} + +public class SingletonPatternDemo { + public static void main(String[] args) { + System.out.println("=== Singleton Pattern Demo - Application Logger ===\n"); + + // Demonstrate singleton behavior + System.out.println("1. Demonstrating Singleton Behavior:"); + Logger logger1 = Logger.getInstance(); + Logger logger2 = Logger.getInstance(); + + System.out.println("Logger1 == Logger2: " + (logger1 == logger2)); + System.out.println("Logger1.hashCode(): " + logger1.hashCode()); + System.out.println("Logger2.hashCode(): " + logger2.hashCode()); + + // Configure the logger + logger1.info("SingletonPatternDemo", "Starting singleton pattern demonstration"); + logger1.setLogLevel(Logger.LogLevel.DEBUG); + logger1.enableFileOutput(true, "demo.log"); + + System.out.println("\n" + logger1.getConfiguration()); + + System.out.println("\n2. Basic Logging Operations:"); + + // Basic logging + logger1.debug("SingletonPatternDemo", "This is a debug message"); + logger1.info("SingletonPatternDemo", "Application started successfully"); + logger1.warn("SingletonPatternDemo", "This is a warning message"); + logger1.error("SingletonPatternDemo", "This is an error message"); + + System.out.println("\n3. Real-world Usage Examples:"); + + // Database operations + DatabaseConnection db = new DatabaseConnection("jdbc:mysql://localhost:3306/testdb"); + try { + db.connect(); + db.executeQuery("SELECT * FROM users"); + db.executeQuery("INSERT INTO users (name) VALUES ('John')"); + db.disconnect(); + } catch (Exception e) { + Logger.getInstance().fatal("SingletonPatternDemo", "Database operation failed: " + e.getMessage()); + } + + // User service operations + UserService userService = new UserService(); + try { + userService.createUser("john_doe", "john@example.com"); + userService.createUser("jane_smith", "invalid-email"); + userService.deleteUser("old_user"); + } catch (Exception e) { + Logger.getInstance().error("SingletonPatternDemo", "User service error: " + e.getMessage()); + } + + System.out.println("\n4. Thread Safety Demonstration:"); + + // Test thread safety with multiple threads + ExecutorService executor = Executors.newFixedThreadPool(5); + + // Submit multiple logging tasks + for (int i = 1; i <= 3; i++) { + executor.submit(new LoggingWorker("Worker-" + i, 3)); + } + + // Wait for completion + executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Logger.getInstance().error("SingletonPatternDemo", "Thread execution interrupted"); + } + + System.out.println("\n5. Logger Statistics:"); + Logger logger = Logger.getInstance(); + System.out.println("Total log entries: " + logger.getLogCount()); + + System.out.println("\nRecent log entries (last 5):"); + List recentLogs = logger.getRecentLogs(5); + for (String logEntry : recentLogs) { + System.out.println(" " + logEntry); + } + + System.out.println("\n6. Testing Different Log Levels:"); + + // Test different log levels + logger.setLogLevel(Logger.LogLevel.WARN); + logger.debug("SingletonPatternDemo", "This debug message should not appear"); + logger.info("SingletonPatternDemo", "This info message should not appear"); + logger.warn("SingletonPatternDemo", "This warning message should appear"); + logger.error("SingletonPatternDemo", "This error message should appear"); + + // Reset to info level + logger.setLogLevel(Logger.LogLevel.INFO); + + System.out.println("\n7. Configuration Summary:"); + System.out.println(logger.getConfiguration()); + + // Test clone prevention + System.out.println("\n8. Testing Clone Prevention:"); + try { + Logger clonedLogger = (Logger) logger.clone(); + } catch (CloneNotSupportedException e) { + System.out.println("✅ Clone prevention working: " + e.getMessage()); + } + + // Final statistics + logger.info("SingletonPatternDemo", "Demonstration completed successfully"); + System.out.println("\nFinal log count: " + logger.getLogCount()); + + System.out.println("\n✅ Singleton pattern successfully demonstrated!"); + System.out.println("Benefits: Single instance, global access, thread safety, resource management"); + + // Cleanup + logger.enableFileOutput(false); + } +} diff --git a/src/content/design-pattern/code/singleton/solution.py b/src/content/design-pattern/code/singleton/solution.py new file mode 100644 index 0000000..8fc883b --- /dev/null +++ b/src/content/design-pattern/code/singleton/solution.py @@ -0,0 +1,509 @@ +""" +Singleton Pattern - Configuration Manager System +Ensures only one instance of the configuration manager exists with thread safety and lazy initialization +""" + +import threading +import json +import os +import time +from datetime import datetime +from typing import Dict, Any, Optional +from enum import Enum + +class ConfigurationManager: + """Thread-safe Singleton Configuration Manager""" + + _instance = None + _lock = threading.Lock() + + class Environment(Enum): + DEVELOPMENT = "development" + STAGING = "staging" + PRODUCTION = "production" + + def __new__(cls): + # Thread-safe singleton implementation using double-checked locking + if cls._instance is None: + with cls._lock: + if cls._instance is None: + cls._instance = super(ConfigurationManager, cls).__new__(cls) + cls._instance._initialized = False + return cls._instance + + def __init__(self): + # Ensure initialization happens only once + if self._initialized: + return + + with self._lock: + if self._initialized: + return + + # Initialize configuration state + self._config_data: Dict[str, Any] = {} + self._environment = self.Environment.DEVELOPMENT + self._config_file_path = "config.json" + self._last_modified = None + self._access_count = 0 + self._access_history = [] + self._watchers = [] + + # Load default configuration + self._load_default_config() + self._initialized = True + + print(f"🔧 ConfigurationManager initialized on thread: {threading.current_thread().name}") + + def _load_default_config(self): + """Load default configuration values""" + self._config_data = { + "app": { + "name": "MyApplication", + "version": "1.0.0", + "debug": True + }, + "database": { + "host": "localhost", + "port": 5432, + "name": "myapp_db", + "pool_size": 10, + "timeout": 30 + }, + "cache": { + "enabled": True, + "ttl": 3600, + "max_size": 1000 + }, + "logging": { + "level": "INFO", + "file_enabled": True, + "console_enabled": True + }, + "security": { + "jwt_expiry": 86400, + "password_min_length": 8, + "rate_limit": 100 + } + } + self._last_modified = datetime.now() + + @classmethod + def get_instance(cls) -> 'ConfigurationManager': + """Get the singleton instance""" + return cls() + + def get(self, key: str, default: Any = None) -> Any: + """Get configuration value by key (supports dot notation)""" + with self._lock: + self._access_count += 1 + self._access_history.append({ + 'key': key, + 'timestamp': datetime.now(), + 'thread': threading.current_thread().name + }) + + # Support dot notation (e.g., "database.host") + keys = key.split('.') + value = self._config_data + + try: + for k in keys: + value = value[k] + return value + except (KeyError, TypeError): + return default + + def set(self, key: str, value: Any) -> None: + """Set configuration value by key (supports dot notation)""" + with self._lock: + keys = key.split('.') + config = self._config_data + + # Navigate to the parent dictionary + for k in keys[:-1]: + if k not in config: + config[k] = {} + config = config[k] + + # Set the final value + old_value = config.get(keys[-1]) + config[keys[-1]] = value + self._last_modified = datetime.now() + + print(f"🔄 Config updated: {key} = {value} (was: {old_value})") + + # Notify watchers + self._notify_watchers(key, old_value, value) + + def get_section(self, section: str) -> Dict[str, Any]: + """Get entire configuration section""" + return self.get(section, {}) + + def set_environment(self, environment: Environment) -> None: + """Set application environment and adjust configurations accordingly""" + with self._lock: + old_env = self._environment + self._environment = environment + + # Adjust configurations based on environment + if environment == self.Environment.PRODUCTION: + self.set("app.debug", False) + self.set("logging.level", "WARN") + self.set("database.pool_size", 50) + self.set("security.rate_limit", 1000) + elif environment == self.Environment.STAGING: + self.set("app.debug", False) + self.set("logging.level", "INFO") + self.set("database.pool_size", 20) + else: # DEVELOPMENT + self.set("app.debug", True) + self.set("logging.level", "DEBUG") + self.set("database.pool_size", 5) + + print(f"🌍 Environment changed from {old_env.value} to {environment.value}") + + def get_environment(self) -> Environment: + """Get current environment""" + return self._environment + + def load_from_file(self, file_path: str) -> bool: + """Load configuration from JSON file""" + try: + with self._lock: + if os.path.exists(file_path): + with open(file_path, 'r') as file: + file_config = json.load(file) + self._merge_config(file_config) + self._config_file_path = file_path + self._last_modified = datetime.now() + print(f"📁 Configuration loaded from {file_path}") + return True + else: + print(f"⚠️ Configuration file not found: {file_path}") + return False + except Exception as e: + print(f"❌ Error loading configuration: {e}") + return False + + def save_to_file(self, file_path: Optional[str] = None) -> bool: + """Save current configuration to JSON file""" + try: + with self._lock: + path = file_path or self._config_file_path + with open(path, 'w') as file: + json.dump(self._config_data, file, indent=2, default=str) + print(f"💾 Configuration saved to {path}") + return True + except Exception as e: + print(f"❌ Error saving configuration: {e}") + return False + + def _merge_config(self, new_config: Dict[str, Any]) -> None: + """Merge new configuration with existing one""" + def merge_dicts(base: Dict, update: Dict): + for key, value in update.items(): + if key in base and isinstance(base[key], dict) and isinstance(value, dict): + merge_dicts(base[key], value) + else: + base[key] = value + + merge_dicts(self._config_data, new_config) + + def add_watcher(self, callback) -> None: + """Add a callback to be notified when configuration changes""" + with self._lock: + self._watchers.append(callback) + print(f"👁️ Added configuration watcher: {callback.__name__}") + + def remove_watcher(self, callback) -> None: + """Remove a configuration watcher""" + with self._lock: + if callback in self._watchers: + self._watchers.remove(callback) + print(f"👁️ Removed configuration watcher: {callback.__name__}") + + def _notify_watchers(self, key: str, old_value: Any, new_value: Any) -> None: + """Notify all watchers about configuration changes""" + for callback in self._watchers: + try: + callback(key, old_value, new_value) + except Exception as e: + print(f"⚠️ Error in watcher {callback.__name__}: {e}") + + def get_statistics(self) -> Dict[str, Any]: + """Get configuration manager statistics""" + with self._lock: + return { + "environment": self._environment.value, + "config_file": self._config_file_path, + "last_modified": self._last_modified.isoformat() if self._last_modified else None, + "access_count": self._access_count, + "total_configs": self._count_configs(self._config_data), + "active_watchers": len(self._watchers), + "thread_id": threading.current_thread().name + } + + def _count_configs(self, config: Dict[str, Any]) -> int: + """Recursively count configuration entries""" + count = 0 + for value in config.values(): + if isinstance(value, dict): + count += self._count_configs(value) + else: + count += 1 + return count + + def get_access_history(self, limit: int = 10) -> list: + """Get recent configuration access history""" + with self._lock: + return self._access_history[-limit:] if self._access_history else [] + + def reload(self) -> None: + """Reload configuration from file""" + if self._config_file_path: + self.load_from_file(self._config_file_path) + + def reset_to_defaults(self) -> None: + """Reset configuration to default values""" + with self._lock: + old_config = self._config_data.copy() + self._load_default_config() + print("🔄 Configuration reset to defaults") + + # Notify watchers about the reset + for callback in self._watchers: + try: + callback("__reset__", old_config, self._config_data) + except Exception as e: + print(f"⚠️ Error in watcher during reset: {e}") + + def __str__(self) -> str: + """String representation of configuration""" + return json.dumps(self._config_data, indent=2, default=str) + +# Example classes demonstrating configuration usage +class DatabaseService: + """Example service using configuration""" + + def __init__(self): + self.config = ConfigurationManager.get_instance() + self.connection = None + + def connect(self): + """Connect to database using configuration""" + host = self.config.get("database.host", "localhost") + port = self.config.get("database.port", 5432) + db_name = self.config.get("database.name", "default") + pool_size = self.config.get("database.pool_size", 10) + timeout = self.config.get("database.timeout", 30) + + print(f"🗄️ Connecting to database: {host}:{port}/{db_name}") + print(f" Pool size: {pool_size}, Timeout: {timeout}s") + + # Simulate connection + time.sleep(0.1) + self.connection = f"Connected to {host}:{port}/{db_name}" + print("✅ Database connected successfully") + + def get_connection_info(self) -> str: + return self.connection or "Not connected" + +class CacheService: + """Example cache service using configuration""" + + def __init__(self): + self.config = ConfigurationManager.get_instance() + self.cache = {} + + def initialize(self): + """Initialize cache with configuration settings""" + enabled = self.config.get("cache.enabled", True) + ttl = self.config.get("cache.ttl", 3600) + max_size = self.config.get("cache.max_size", 1000) + + if enabled: + print(f"🧠 Initializing cache: TTL={ttl}s, Max size={max_size}") + self.cache = {"ttl": ttl, "max_size": max_size, "entries": {}} + else: + print("🚫 Cache disabled by configuration") + + def get_cache_info(self) -> Dict[str, Any]: + return self.cache + +class LoggerService: + """Example logger service using configuration""" + + def __init__(self): + self.config = ConfigurationManager.get_instance() + + def setup_logging(self): + """Setup logging based on configuration""" + level = self.config.get("logging.level", "INFO") + file_enabled = self.config.get("logging.file_enabled", True) + console_enabled = self.config.get("logging.console_enabled", True) + + print(f"📝 Setting up logging: Level={level}") + print(f" File output: {'Enabled' if file_enabled else 'Disabled'}") + print(f" Console output: {'Enabled' if console_enabled else 'Disabled'}") + +# Configuration change watcher example +def config_change_watcher(key: str, old_value: Any, new_value: Any): + """Example configuration change watcher""" + print(f"🔔 Config change detected: {key} changed from {old_value} to {new_value}") + +# Thread worker to test thread safety +def config_worker(worker_id: int, operation_count: int): + """Worker function to test thread safety""" + config = ConfigurationManager.get_instance() + + for i in range(operation_count): + # Read operations + app_name = config.get("app.name") + db_host = config.get("database.host") + + # Write operations (with unique values per worker) + config.set(f"worker_{worker_id}.counter", i) + config.set(f"worker_{worker_id}.timestamp", datetime.now().isoformat()) + + time.sleep(0.01) # Small delay to allow thread interleaving + + print(f"✅ Worker {worker_id} completed {operation_count} operations") + +def main(): + print("=== Singleton Pattern Demo - Configuration Manager ===\n") + + # Demonstrate singleton behavior + print("1. Demonstrating Singleton Behavior:") + config1 = ConfigurationManager.get_instance() + config2 = ConfigurationManager() + config3 = ConfigurationManager.get_instance() + + print(f"config1 is config2: {config1 is config2}") + print(f"config1 is config3: {config1 is config3}") + print(f"Instance ID: {id(config1)}") + + # Basic configuration operations + print("\n2. Basic Configuration Operations:") + config = ConfigurationManager.get_instance() + + print(f"App name: {config.get('app.name')}") + print(f"Database host: {config.get('database.host')}") + print(f"Non-existent key: {config.get('non.existent', 'default_value')}") + + # Set new values + config.set("app.version", "2.0.0") + config.set("new.feature.enabled", True) + print(f"Updated app version: {config.get('app.version')}") + + # Environment switching + print("\n3. Environment Management:") + print(f"Current environment: {config.get_environment().value}") + + config.set_environment(ConfigurationManager.Environment.PRODUCTION) + print(f"Debug mode after production switch: {config.get('app.debug')}") + print(f"Database pool size: {config.get('database.pool_size')}") + + config.set_environment(ConfigurationManager.Environment.DEVELOPMENT) + print(f"Debug mode after development switch: {config.get('app.debug')}") + + # Configuration watchers + print("\n4. Configuration Watchers:") + config.add_watcher(config_change_watcher) + + config.set("test.value", "initial") + config.set("test.value", "updated") + config.set("test.another", "new_value") + + # Services using configuration + print("\n5. Services Using Configuration:") + + db_service = DatabaseService() + db_service.connect() + print(f"Connection: {db_service.get_connection_info()}") + + cache_service = CacheService() + cache_service.initialize() + print(f"Cache info: {cache_service.get_cache_info()}") + + logger_service = LoggerService() + logger_service.setup_logging() + + # Thread safety demonstration + print("\n6. Thread Safety Demonstration:") + + threads = [] + thread_count = 5 + operations_per_thread = 10 + + start_time = time.time() + + # Create and start threads + for i in range(thread_count): + thread = threading.Thread( + target=config_worker, + args=(i, operations_per_thread) + ) + threads.append(thread) + thread.start() + + # Wait for all threads to complete + for thread in threads: + thread.join() + + end_time = time.time() + + print(f"⏱️ Thread test completed in {end_time - start_time:.2f}s") + + # Statistics and history + print("\n7. Configuration Statistics:") + stats = config.get_statistics() + for key, value in stats.items(): + print(f" {key}: {value}") + + print("\nRecent access history:") + history = config.get_access_history(5) + for access in history: + print(f" {access['timestamp']}: {access['key']} (thread: {access['thread']})") + + # File operations + print("\n8. File Operations:") + + # Save current configuration + config.save_to_file("demo_config.json") + + # Modify and reload + config.set("temp.value", "temporary") + print(f"Temp value before reload: {config.get('temp.value')}") + + config.reload() + print(f"Temp value after reload: {config.get('temp.value', 'Not found')}") + + # Configuration sections + print("\n9. Configuration Sections:") + database_config = config.get_section("database") + print("Database configuration:") + for key, value in database_config.items(): + print(f" {key}: {value}") + + # Reset demonstration + print("\n10. Reset to Defaults:") + total_configs_before = config.get_statistics()["total_configs"] + config.reset_to_defaults() + total_configs_after = config.get_statistics()["total_configs"] + + print(f"Configs before reset: {total_configs_before}") + print(f"Configs after reset: {total_configs_after}") + + # Cleanup + config.remove_watcher(config_change_watcher) + + print(f"\n✅ Singleton pattern successfully demonstrated!") + print("Benefits: Single instance, thread safety, global configuration, change notification") + + # Final statistics + final_stats = config.get_statistics() + print(f"\nFinal access count: {final_stats['access_count']}") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/state/solution.cpp b/src/content/design-pattern/code/state/solution.cpp new file mode 100644 index 0000000..490190b --- /dev/null +++ b/src/content/design-pattern/code/state/solution.cpp @@ -0,0 +1,287 @@ +#include +#include +#include + +// Forward declaration +class MediaPlayer; + +// State interface +class MediaPlayerState { +public: + virtual ~MediaPlayerState() = default; + virtual void play(MediaPlayer& context) = 0; + virtual void pause(MediaPlayer& context) = 0; + virtual void stop(MediaPlayer& context) = 0; + virtual void next(MediaPlayer& context) = 0; + virtual void previous(MediaPlayer& context) = 0; + virtual std::string getStateName() = 0; +}; + +// Context class +class MediaPlayer { +private: + std::unique_ptr currentState; + std::string currentTrack; + int trackNumber; + int volume; + +public: + MediaPlayer(); + + void setState(std::unique_ptr state) { + this->currentState = std::move(state); + std::cout << "🔄 State changed to: " << this->currentState->getStateName() << std::endl; + } + + // Delegate operations to current state + void play() { + currentState->play(*this); + } + + void pause() { + currentState->pause(*this); + } + + void stop() { + currentState->stop(*this); + } + + void next() { + currentState->next(*this); + } + + void previous() { + currentState->previous(*this); + } + + // Getters and setters + std::string getCurrentTrack() const { + return currentTrack; + } + + void setCurrentTrack(const std::string& track) { + this->currentTrack = track; + } + + int getTrackNumber() const { + return trackNumber; + } + + void setTrackNumber(int number) { + this->trackNumber = number; + } + + int getVolume() const { + return volume; + } + + void setVolume(int volume) { + this->volume = volume; + } + + std::string getStateName() { + return currentState->getStateName(); + } + + void displayStatus() { + std::cout << "━━━ MEDIA PLAYER STATUS ━━━" << std::endl; + std::cout << "State: " << getStateName() << std::endl; + std::cout << "Track: " << currentTrack << std::endl; + std::cout << "Track #: " << trackNumber << std::endl; + std::cout << "Volume: " << volume << "%" << std::endl; + std::cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" << std::endl; + } +}; + +// Forward declarations for states +class StoppedState; +class PlayingState; +class PausedState; + +// Concrete States +class StoppedState : public MediaPlayerState { +private: + void loadTrack(MediaPlayer& context, int trackNumber) { + context.setTrackNumber(trackNumber); + context.setCurrentTrack("Song " + std::to_string(trackNumber) + + " - Artist " + std::to_string(trackNumber)); + std::cout << "🎵 Loaded: " << context.getCurrentTrack() << std::endl; + } + +public: + void play(MediaPlayer& context) override { + std::cout << "▶️ Starting playback..." << std::endl; + loadTrack(context, 1); + context.setState(std::make_unique()); + } + + void pause(MediaPlayer& context) override { + std::cout << "⏸️ Cannot pause - player is stopped" << std::endl; + } + + void stop(MediaPlayer& context) override { + std::cout << "⏹️ Player is already stopped" << std::endl; + } + + void next(MediaPlayer& context) override { + std::cout << "⏭️ Loading next track..." << std::endl; + loadTrack(context, context.getTrackNumber() + 1); + } + + void previous(MediaPlayer& context) override { + std::cout << "⏮️ Loading previous track..." << std::endl; + if (context.getTrackNumber() > 1) { + loadTrack(context, context.getTrackNumber() - 1); + } + } + + std::string getStateName() override { + return "Stopped"; + } +}; + +class PlayingState : public MediaPlayerState { +public: + void play(MediaPlayer& context) override { + std::cout << "▶️ Already playing: " << context.getCurrentTrack() << std::endl; + } + + void pause(MediaPlayer& context) override { + std::cout << "⏸️ Pausing playback..." << std::endl; + context.setState(std::make_unique()); + } + + void stop(MediaPlayer& context) override { + std::cout << "⏹️ Stopping playback..." << std::endl; + context.setState(std::make_unique()); + } + + void next(MediaPlayer& context) override { + std::cout << "⏭️ Skipping to next track..." << std::endl; + int nextTrack = context.getTrackNumber() + 1; + context.setTrackNumber(nextTrack); + context.setCurrentTrack("Song " + std::to_string(nextTrack) + + " - Artist " + std::to_string(nextTrack)); + std::cout << "🎵 Now playing: " << context.getCurrentTrack() << std::endl; + } + + void previous(MediaPlayer& context) override { + std::cout << "⏮️ Going to previous track..." << std::endl; + if (context.getTrackNumber() > 1) { + int prevTrack = context.getTrackNumber() - 1; + context.setTrackNumber(prevTrack); + context.setCurrentTrack("Song " + std::to_string(prevTrack) + + " - Artist " + std::to_string(prevTrack)); + std::cout << "🎵 Now playing: " << context.getCurrentTrack() << std::endl; + } else { + std::cout << "🔚 Already at first track" << std::endl; + } + } + + std::string getStateName() override { + return "Playing"; + } +}; + +class PausedState : public MediaPlayerState { +public: + void play(MediaPlayer& context) override { + std::cout << "▶️ Resuming playback..." << std::endl; + context.setState(std::make_unique()); + } + + void pause(MediaPlayer& context) override { + std::cout << "⏸️ Already paused" << std::endl; + } + + void stop(MediaPlayer& context) override { + std::cout << "⏹️ Stopping from paused state..." << std::endl; + context.setState(std::make_unique()); + } + + void next(MediaPlayer& context) override { + std::cout << "⏭️ Loading next track (will remain paused)..." << std::endl; + int nextTrack = context.getTrackNumber() + 1; + context.setTrackNumber(nextTrack); + context.setCurrentTrack("Song " + std::to_string(nextTrack) + + " - Artist " + std::to_string(nextTrack)); + std::cout << "🎵 Loaded: " << context.getCurrentTrack() << std::endl; + } + + void previous(MediaPlayer& context) override { + std::cout << "⏮️ Loading previous track (will remain paused)..." << std::endl; + if (context.getTrackNumber() > 1) { + int prevTrack = context.getTrackNumber() - 1; + context.setTrackNumber(prevTrack); + context.setCurrentTrack("Song " + std::to_string(prevTrack) + + " - Artist " + std::to_string(prevTrack)); + std::cout << "🎵 Loaded: " << context.getCurrentTrack() << std::endl; + } + } + + std::string getStateName() override { + return "Paused"; + } +}; + +// MediaPlayer constructor implementation +MediaPlayer::MediaPlayer() { + this->currentState = std::make_unique(); + this->currentTrack = "No track selected"; + this->trackNumber = 0; + this->volume = 50; +} + +int main() { + std::cout << "=== MEDIA PLAYER STATE PATTERN DEMO ===\n" << std::endl; + + MediaPlayer player; + + // Initial state + player.displayStatus(); + + // Test different state transitions + std::cout << "1. STARTING PLAYBACK" << std::endl; + player.play(); + player.displayStatus(); + + std::cout << "2. SKIPPING TRACKS WHILE PLAYING" << std::endl; + player.next(); + player.next(); + player.displayStatus(); + + std::cout << "3. PAUSING PLAYBACK" << std::endl; + player.pause(); + player.displayStatus(); + + std::cout << "4. TRYING TO PAUSE AGAIN" << std::endl; + player.pause(); + + std::cout << "5. NAVIGATING WHILE PAUSED" << std::endl; + player.previous(); + player.displayStatus(); + + std::cout << "6. RESUMING PLAYBACK" << std::endl; + player.play(); + player.displayStatus(); + + std::cout << "7. STOPPING PLAYBACK" << std::endl; + player.stop(); + player.displayStatus(); + + std::cout << "8. TRYING TO PAUSE WHEN STOPPED" << std::endl; + player.pause(); + + std::cout << "9. NAVIGATING WHEN STOPPED" << std::endl; + player.next(); + player.displayStatus(); + + std::cout << "=== STATE PATTERN BENEFITS ===" << std::endl; + std::cout << "✓ Eliminates complex conditional logic" << std::endl; + std::cout << "✓ Each state handles its own behavior" << std::endl; + std::cout << "✓ Easy to add new states without modifying existing code" << std::endl; + std::cout << "✓ State transitions are explicit and clear" << std::endl; + std::cout << "✓ Follows Single Responsibility Principle" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/state/solution.java b/src/content/design-pattern/code/state/solution.java new file mode 100644 index 0000000..b183ec0 --- /dev/null +++ b/src/content/design-pattern/code/state/solution.java @@ -0,0 +1,278 @@ +// State interface +interface MediaPlayerState { + void play(MediaPlayer context); + void pause(MediaPlayer context); + void stop(MediaPlayer context); + void next(MediaPlayer context); + void previous(MediaPlayer context); + String getStateName(); +} + +// Context class +class MediaPlayer { + private MediaPlayerState currentState; + private String currentTrack; + private int trackNumber; + private int volume; + + public MediaPlayer() { + this.currentState = new StoppedState(); + this.currentTrack = "No track selected"; + this.trackNumber = 0; + this.volume = 50; + } + + public void setState(MediaPlayerState state) { + this.currentState = state; + System.out.println("🔄 State changed to: " + state.getStateName()); + } + + // Delegate operations to current state + public void play() { + currentState.play(this); + } + + public void pause() { + currentState.pause(this); + } + + public void stop() { + currentState.stop(this); + } + + public void next() { + currentState.next(this); + } + + public void previous() { + currentState.previous(this); + } + + // Getters and setters + public String getCurrentTrack() { + return currentTrack; + } + + public void setCurrentTrack(String track) { + this.currentTrack = track; + } + + public int getTrackNumber() { + return trackNumber; + } + + public void setTrackNumber(int number) { + this.trackNumber = number; + } + + public int getVolume() { + return volume; + } + + public void setVolume(int volume) { + this.volume = volume; + } + + public String getStateName() { + return currentState.getStateName(); + } + + public void displayStatus() { + System.out.println("━━━ MEDIA PLAYER STATUS ━━━"); + System.out.println("State: " + getStateName()); + System.out.println("Track: " + currentTrack); + System.out.println("Track #: " + trackNumber); + System.out.println("Volume: " + volume + "%"); + System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); + } +} + +// Concrete States +class StoppedState implements MediaPlayerState { + @Override + public void play(MediaPlayer context) { + System.out.println("▶️ Starting playback..."); + loadTrack(context, 1); + context.setState(new PlayingState()); + } + + @Override + public void pause(MediaPlayer context) { + System.out.println("⏸️ Cannot pause - player is stopped"); + } + + @Override + public void stop(MediaPlayer context) { + System.out.println("⏹️ Player is already stopped"); + } + + @Override + public void next(MediaPlayer context) { + System.out.println("⏭️ Loading next track..."); + loadTrack(context, context.getTrackNumber() + 1); + } + + @Override + public void previous(MediaPlayer context) { + System.out.println("⏮️ Loading previous track..."); + if (context.getTrackNumber() > 1) { + loadTrack(context, context.getTrackNumber() - 1); + } + } + + private void loadTrack(MediaPlayer context, int trackNumber) { + context.setTrackNumber(trackNumber); + context.setCurrentTrack("Song " + trackNumber + " - Artist " + trackNumber); + System.out.println("🎵 Loaded: " + context.getCurrentTrack()); + } + + @Override + public String getStateName() { + return "Stopped"; + } +} + +class PlayingState implements MediaPlayerState { + @Override + public void play(MediaPlayer context) { + System.out.println("▶️ Already playing: " + context.getCurrentTrack()); + } + + @Override + public void pause(MediaPlayer context) { + System.out.println("⏸️ Pausing playback..."); + context.setState(new PausedState()); + } + + @Override + public void stop(MediaPlayer context) { + System.out.println("⏹️ Stopping playback..."); + context.setState(new StoppedState()); + } + + @Override + public void next(MediaPlayer context) { + System.out.println("⏭️ Skipping to next track..."); + int nextTrack = context.getTrackNumber() + 1; + context.setTrackNumber(nextTrack); + context.setCurrentTrack("Song " + nextTrack + " - Artist " + nextTrack); + System.out.println("🎵 Now playing: " + context.getCurrentTrack()); + } + + @Override + public void previous(MediaPlayer context) { + System.out.println("⏮️ Going to previous track..."); + if (context.getTrackNumber() > 1) { + int prevTrack = context.getTrackNumber() - 1; + context.setTrackNumber(prevTrack); + context.setCurrentTrack("Song " + prevTrack + " - Artist " + prevTrack); + System.out.println("🎵 Now playing: " + context.getCurrentTrack()); + } else { + System.out.println("🔚 Already at first track"); + } + } + + @Override + public String getStateName() { + return "Playing"; + } +} + +class PausedState implements MediaPlayerState { + @Override + public void play(MediaPlayer context) { + System.out.println("▶️ Resuming playback..."); + context.setState(new PlayingState()); + } + + @Override + public void pause(MediaPlayer context) { + System.out.println("⏸️ Already paused"); + } + + @Override + public void stop(MediaPlayer context) { + System.out.println("⏹️ Stopping from paused state..."); + context.setState(new StoppedState()); + } + + @Override + public void next(MediaPlayer context) { + System.out.println("⏭️ Loading next track (will remain paused)..."); + int nextTrack = context.getTrackNumber() + 1; + context.setTrackNumber(nextTrack); + context.setCurrentTrack("Song " + nextTrack + " - Artist " + nextTrack); + System.out.println("🎵 Loaded: " + context.getCurrentTrack()); + } + + @Override + public void previous(MediaPlayer context) { + System.out.println("⏮️ Loading previous track (will remain paused)..."); + if (context.getTrackNumber() > 1) { + int prevTrack = context.getTrackNumber() - 1; + context.setTrackNumber(prevTrack); + context.setCurrentTrack("Song " + prevTrack + " - Artist " + prevTrack); + System.out.println("🎵 Loaded: " + context.getCurrentTrack()); + } + } + + @Override + public String getStateName() { + return "Paused"; + } +} + +// Demo class +public class StatePatternDemo { + public static void main(String[] args) { + System.out.println("=== MEDIA PLAYER STATE PATTERN DEMO ===\n"); + + MediaPlayer player = new MediaPlayer(); + + // Initial state + player.displayStatus(); + + // Test different state transitions + System.out.println("1. STARTING PLAYBACK"); + player.play(); + player.displayStatus(); + + System.out.println("2. SKIPPING TRACKS WHILE PLAYING"); + player.next(); + player.next(); + player.displayStatus(); + + System.out.println("3. PAUSING PLAYBACK"); + player.pause(); + player.displayStatus(); + + System.out.println("4. TRYING TO PAUSE AGAIN"); + player.pause(); + + System.out.println("5. NAVIGATING WHILE PAUSED"); + player.previous(); + player.displayStatus(); + + System.out.println("6. RESUMING PLAYBACK"); + player.play(); + player.displayStatus(); + + System.out.println("7. STOPPING PLAYBACK"); + player.stop(); + player.displayStatus(); + + System.out.println("8. TRYING TO PAUSE WHEN STOPPED"); + player.pause(); + + System.out.println("9. NAVIGATING WHEN STOPPED"); + player.next(); + player.displayStatus(); + + System.out.println("=== STATE PATTERN BENEFITS ==="); + System.out.println("✓ Eliminates complex conditional logic"); + System.out.println("✓ Each state handles its own behavior"); + System.out.println("✓ Easy to add new states without modifying existing code"); + System.out.println("✓ State transitions are explicit and clear"); + System.out.println("✓ Follows Single Responsibility Principle"); + } +} diff --git a/src/content/design-pattern/code/state/solution.py b/src/content/design-pattern/code/state/solution.py new file mode 100644 index 0000000..0d1f7cb --- /dev/null +++ b/src/content/design-pattern/code/state/solution.py @@ -0,0 +1,245 @@ +from abc import ABC, abstractmethod +from typing import Optional + +# Forward declaration +class MediaPlayer: + pass + +# State interface +class MediaPlayerState(ABC): + @abstractmethod + def play(self, context: MediaPlayer) -> None: + pass + + @abstractmethod + def pause(self, context: MediaPlayer) -> None: + pass + + @abstractmethod + def stop(self, context: MediaPlayer) -> None: + pass + + @abstractmethod + def next(self, context: MediaPlayer) -> None: + pass + + @abstractmethod + def previous(self, context: MediaPlayer) -> None: + pass + + @abstractmethod + def get_state_name(self) -> str: + pass + +# Context class +class MediaPlayer: + def __init__(self): + from .state_implementations import StoppedState # Import here to avoid circular import + self._current_state: MediaPlayerState = StoppedState() + self._current_track: str = "No track selected" + self._track_number: int = 0 + self._volume: int = 50 + + def set_state(self, state: MediaPlayerState) -> None: + self._current_state = state + print(f"🔄 State changed to: {state.get_state_name()}") + + # Delegate operations to current state + def play(self) -> None: + self._current_state.play(self) + + def pause(self) -> None: + self._current_state.pause(self) + + def stop(self) -> None: + self._current_state.stop(self) + + def next(self) -> None: + self._current_state.next(self) + + def previous(self) -> None: + self._current_state.previous(self) + + # Getters and setters + def get_current_track(self) -> str: + return self._current_track + + def set_current_track(self, track: str) -> None: + self._current_track = track + + def get_track_number(self) -> int: + return self._track_number + + def set_track_number(self, number: int) -> None: + self._track_number = number + + def get_volume(self) -> int: + return self._volume + + def set_volume(self, volume: int) -> None: + self._volume = volume + + def get_state_name(self) -> str: + return self._current_state.get_state_name() + + def display_status(self) -> None: + print("━━━ MEDIA PLAYER STATUS ━━━") + print(f"State: {self.get_state_name()}") + print(f"Track: {self._current_track}") + print(f"Track #: {self._track_number}") + print(f"Volume: {self._volume}%") + print("━━━━━━━━━━━━━━━━━━━━━━━━━━━\n") + +# Concrete States +class StoppedState(MediaPlayerState): + def _load_track(self, context: MediaPlayer, track_number: int) -> None: + context.set_track_number(track_number) + context.set_current_track(f"Song {track_number} - Artist {track_number}") + print(f"🎵 Loaded: {context.get_current_track()}") + + def play(self, context: MediaPlayer) -> None: + print("▶️ Starting playback...") + self._load_track(context, 1) + context.set_state(PlayingState()) + + def pause(self, context: MediaPlayer) -> None: + print("⏸️ Cannot pause - player is stopped") + + def stop(self, context: MediaPlayer) -> None: + print("⏹️ Player is already stopped") + + def next(self, context: MediaPlayer) -> None: + print("⏭️ Loading next track...") + self._load_track(context, context.get_track_number() + 1) + + def previous(self, context: MediaPlayer) -> None: + print("⏮️ Loading previous track...") + if context.get_track_number() > 1: + self._load_track(context, context.get_track_number() - 1) + + def get_state_name(self) -> str: + return "Stopped" + +class PlayingState(MediaPlayerState): + def play(self, context: MediaPlayer) -> None: + print(f"▶️ Already playing: {context.get_current_track()}") + + def pause(self, context: MediaPlayer) -> None: + print("⏸️ Pausing playback...") + context.set_state(PausedState()) + + def stop(self, context: MediaPlayer) -> None: + print("⏹️ Stopping playback...") + context.set_state(StoppedState()) + + def next(self, context: MediaPlayer) -> None: + print("⏭️ Skipping to next track...") + next_track = context.get_track_number() + 1 + context.set_track_number(next_track) + context.set_current_track(f"Song {next_track} - Artist {next_track}") + print(f"🎵 Now playing: {context.get_current_track()}") + + def previous(self, context: MediaPlayer) -> None: + print("⏮️ Going to previous track...") + if context.get_track_number() > 1: + prev_track = context.get_track_number() - 1 + context.set_track_number(prev_track) + context.set_current_track(f"Song {prev_track} - Artist {prev_track}") + print(f"🎵 Now playing: {context.get_current_track()}") + else: + print("🔚 Already at first track") + + def get_state_name(self) -> str: + return "Playing" + +class PausedState(MediaPlayerState): + def play(self, context: MediaPlayer) -> None: + print("▶️ Resuming playback...") + context.set_state(PlayingState()) + + def pause(self, context: MediaPlayer) -> None: + print("⏸️ Already paused") + + def stop(self, context: MediaPlayer) -> None: + print("⏹️ Stopping from paused state...") + context.set_state(StoppedState()) + + def next(self, context: MediaPlayer) -> None: + print("⏭️ Loading next track (will remain paused)...") + next_track = context.get_track_number() + 1 + context.set_track_number(next_track) + context.set_current_track(f"Song {next_track} - Artist {next_track}") + print(f"🎵 Loaded: {context.get_current_track()}") + + def previous(self, context: MediaPlayer) -> None: + print("⏮️ Loading previous track (will remain paused)...") + if context.get_track_number() > 1: + prev_track = context.get_track_number() - 1 + context.set_track_number(prev_track) + context.set_current_track(f"Song {prev_track} - Artist {prev_track}") + print(f"🎵 Loaded: {context.get_current_track()}") + + def get_state_name(self) -> str: + return "Paused" + +# Update MediaPlayer constructor to avoid circular import +MediaPlayer.__init__ = lambda self: ( + setattr(self, '_current_state', StoppedState()), + setattr(self, '_current_track', "No track selected"), + setattr(self, '_track_number', 0), + setattr(self, '_volume', 50) +)[0] or None + +def main(): + print("=== MEDIA PLAYER STATE PATTERN DEMO ===\n") + + player = MediaPlayer() + + # Initial state + player.display_status() + + # Test different state transitions + print("1. STARTING PLAYBACK") + player.play() + player.display_status() + + print("2. SKIPPING TRACKS WHILE PLAYING") + player.next() + player.next() + player.display_status() + + print("3. PAUSING PLAYBACK") + player.pause() + player.display_status() + + print("4. TRYING TO PAUSE AGAIN") + player.pause() + + print("5. NAVIGATING WHILE PAUSED") + player.previous() + player.display_status() + + print("6. RESUMING PLAYBACK") + player.play() + player.display_status() + + print("7. STOPPING PLAYBACK") + player.stop() + player.display_status() + + print("8. TRYING TO PAUSE WHEN STOPPED") + player.pause() + + print("9. NAVIGATING WHEN STOPPED") + player.next() + player.display_status() + + print("=== STATE PATTERN BENEFITS ===") + print("✓ Eliminates complex conditional logic") + print("✓ Each state handles its own behavior") + print("✓ Easy to add new states without modifying existing code") + print("✓ State transitions are explicit and clear") + print("✓ Follows Single Responsibility Principle") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/strategy/solution.cpp b/src/content/design-pattern/code/strategy/solution.cpp new file mode 100644 index 0000000..0bd4c35 --- /dev/null +++ b/src/content/design-pattern/code/strategy/solution.cpp @@ -0,0 +1,169 @@ +#include +#include +#include + +// Strategy interface +class PaymentStrategy { +public: + virtual ~PaymentStrategy() = default; + virtual void pay(double amount) = 0; + virtual bool validate() = 0; +}; + +// Concrete Strategies +class CreditCardPayment : public PaymentStrategy { +private: + std::string cardNumber; + std::string holderName; + std::string cvv; + +public: + CreditCardPayment(const std::string& cardNumber, const std::string& holderName, const std::string& cvv) + : cardNumber(cardNumber), holderName(holderName), cvv(cvv) {} + + bool validate() override { + return !cardNumber.empty() && cardNumber.length() == 16 && + !cvv.empty() && cvv.length() == 3; + } + + void pay(double amount) override { + if (validate()) { + std::cout << "Processing credit card payment of $" << amount << std::endl; + std::cout << "Card: ****-****-****-" << cardNumber.substr(12) << std::endl; + std::cout << "Holder: " << holderName << std::endl; + std::cout << "Payment successful via Credit Card!" << std::endl; + } else { + std::cout << "Invalid credit card details!" << std::endl; + } + } +}; + +class PayPalPayment : public PaymentStrategy { +private: + std::string email; + std::string password; + +public: + PayPalPayment(const std::string& email, const std::string& password) + : email(email), password(password) {} + + bool validate() override { + return !email.empty() && email.find('@') != std::string::npos && + !password.empty() && password.length() >= 6; + } + + void pay(double amount) override { + if (validate()) { + std::cout << "Connecting to PayPal..." << std::endl; + std::cout << "Processing PayPal payment of $" << amount << std::endl; + std::cout << "Email: " << email << std::endl; + std::cout << "Payment successful via PayPal!" << std::endl; + } else { + std::cout << "Invalid PayPal credentials!" << std::endl; + } + } +}; + +class BankTransferPayment : public PaymentStrategy { +private: + std::string accountNumber; + std::string routingNumber; + +public: + BankTransferPayment(const std::string& accountNumber, const std::string& routingNumber) + : accountNumber(accountNumber), routingNumber(routingNumber) {} + + bool validate() override { + return !accountNumber.empty() && accountNumber.length() >= 8 && + !routingNumber.empty() && routingNumber.length() == 9; + } + + void pay(double amount) override { + if (validate()) { + std::cout << "Initiating bank transfer..." << std::endl; + std::cout << "Processing bank transfer of $" << amount << std::endl; + std::cout << "Account: ****" << accountNumber.substr(accountNumber.length() - 4) << std::endl; + std::cout << "Payment successful via Bank Transfer!" << std::endl; + } else { + std::cout << "Invalid bank account details!" << std::endl; + } + } +}; + +// Context class +class ShoppingCart { +private: + std::unique_ptr paymentStrategy; + double totalAmount; + +public: + ShoppingCart() : totalAmount(0.0) {} + + void setPaymentStrategy(std::unique_ptr strategy) { + paymentStrategy = std::move(strategy); + } + + void addToCart(double itemPrice) { + totalAmount += itemPrice; + std::cout << "Item added. Current total: $" << totalAmount << std::endl; + } + + void checkout() { + if (!paymentStrategy) { + std::cout << "Please select a payment method!" << std::endl; + return; + } + + std::cout << "\n--- CHECKOUT PROCESS ---" << std::endl; + std::cout << "Total amount: $" << totalAmount << std::endl; + paymentStrategy->pay(totalAmount); + std::cout << "Checkout completed!\n" << std::endl; + totalAmount = 0; // Reset cart after payment + } + + double getTotal() const { + return totalAmount; + } +}; + +int main() { + ShoppingCart cart; + + // Add items to cart + std::cout << "=== SHOPPING SESSION ===" << std::endl; + cart.addToCart(29.99); + cart.addToCart(15.50); + cart.addToCart(75.25); + + // Try different payment strategies + std::cout << "\n=== TRYING DIFFERENT PAYMENT METHODS ===" << std::endl; + + // Credit Card Payment + cart.setPaymentStrategy(std::make_unique("1234567890123456", "John Doe", "123")); + cart.checkout(); + + // Add more items + cart.addToCart(99.99); + cart.addToCart(45.00); + + // PayPal Payment + cart.setPaymentStrategy(std::make_unique("john.doe@email.com", "securepass")); + cart.checkout(); + + // More items + cart.addToCart(199.99); + + // Bank Transfer Payment + cart.setPaymentStrategy(std::make_unique("12345678901", "123456789")); + cart.checkout(); + + // Test invalid payment methods + std::cout << "=== TESTING INVALID PAYMENT METHODS ===" << std::endl; + cart.addToCart(25.00); + + // Invalid credit card + cart.setPaymentStrategy(std::make_unique("123", "Invalid User", "12")); + cart.checkout(); + + return 0; +} diff --git a/src/content/design-pattern/code/strategy/solution.java b/src/content/design-pattern/code/strategy/solution.java new file mode 100644 index 0000000..985d4f7 --- /dev/null +++ b/src/content/design-pattern/code/strategy/solution.java @@ -0,0 +1,202 @@ +// Strategy interface +interface PaymentStrategy { + void pay(double amount); + boolean validate(); +} + +// Concrete Strategies +class CreditCardPayment implements PaymentStrategy { + private String cardNumber; + private String holderName; + private String cvv; + + public CreditCardPayment(String cardNumber, String holderName, String cvv) { + this.cardNumber = cardNumber; + this.holderName = holderName; + this.cvv = cvv; + } + + @Override + public boolean validate() { + // Simple validation logic + return cardNumber != null && cardNumber.length() == 16 && + cvv != null && cvv.length() == 3; + } + + @Override + public void pay(double amount) { + if (validate()) { + System.out.println("Processing credit card payment of $" + amount); + System.out.println("Card: ****-****-****-" + cardNumber.substring(12)); + System.out.println("Holder: " + holderName); + System.out.println("Payment successful via Credit Card!"); + } else { + System.out.println("Invalid credit card details!"); + } + } +} + +class PayPalPayment implements PaymentStrategy { + private String email; + private String password; + + public PayPalPayment(String email, String password) { + this.email = email; + this.password = password; + } + + @Override + public boolean validate() { + return email != null && email.contains("@") && + password != null && password.length() >= 6; + } + + @Override + public void pay(double amount) { + if (validate()) { + System.out.println("Connecting to PayPal..."); + System.out.println("Processing PayPal payment of $" + amount); + System.out.println("Email: " + email); + System.out.println("Payment successful via PayPal!"); + } else { + System.out.println("Invalid PayPal credentials!"); + } + } +} + +class BankTransferPayment implements PaymentStrategy { + private String accountNumber; + private String routingNumber; + + public BankTransferPayment(String accountNumber, String routingNumber) { + this.accountNumber = accountNumber; + this.routingNumber = routingNumber; + } + + @Override + public boolean validate() { + return accountNumber != null && accountNumber.length() >= 8 && + routingNumber != null && routingNumber.length() == 9; + } + + @Override + public void pay(double amount) { + if (validate()) { + System.out.println("Initiating bank transfer..."); + System.out.println("Processing bank transfer of $" + amount); + System.out.println("Account: ****" + accountNumber.substring(accountNumber.length() - 4)); + System.out.println("Payment successful via Bank Transfer!"); + } else { + System.out.println("Invalid bank account details!"); + } + } +} + +class CryptocurrencyPayment implements PaymentStrategy { + private String walletAddress; + private String privateKey; + + public CryptocurrencyPayment(String walletAddress, String privateKey) { + this.walletAddress = walletAddress; + this.privateKey = privateKey; + } + + @Override + public boolean validate() { + return walletAddress != null && walletAddress.length() >= 26 && + privateKey != null && privateKey.length() >= 32; + } + + @Override + public void pay(double amount) { + if (validate()) { + System.out.println("Broadcasting cryptocurrency transaction..."); + System.out.println("Processing crypto payment of $" + amount); + System.out.println("Wallet: " + walletAddress.substring(0, 6) + "..."); + System.out.println("Payment successful via Cryptocurrency!"); + } else { + System.out.println("Invalid cryptocurrency wallet details!"); + } + } +} + +// Context class +class ShoppingCart { + private PaymentStrategy paymentStrategy; + private double totalAmount; + + public void setPaymentStrategy(PaymentStrategy paymentStrategy) { + this.paymentStrategy = paymentStrategy; + } + + public void addToCart(double itemPrice) { + totalAmount += itemPrice; + System.out.println("Item added. Current total: $" + totalAmount); + } + + public void checkout() { + if (paymentStrategy == null) { + System.out.println("Please select a payment method!"); + return; + } + + System.out.println("\n--- CHECKOUT PROCESS ---"); + System.out.println("Total amount: $" + totalAmount); + paymentStrategy.pay(totalAmount); + System.out.println("Checkout completed!\n"); + totalAmount = 0; // Reset cart after payment + } + + public double getTotal() { + return totalAmount; + } +} + +public class StrategyPatternDemo { + public static void main(String[] args) { + ShoppingCart cart = new ShoppingCart(); + + // Add items to cart + System.out.println("=== SHOPPING SESSION ==="); + cart.addToCart(29.99); + cart.addToCart(15.50); + cart.addToCart(75.25); + + // Try different payment strategies + System.out.println("\n=== TRYING DIFFERENT PAYMENT METHODS ==="); + + // Credit Card Payment + cart.setPaymentStrategy(new CreditCardPayment("1234567890123456", "John Doe", "123")); + cart.checkout(); + + // Add more items + cart.addToCart(99.99); + cart.addToCart(45.00); + + // PayPal Payment + cart.setPaymentStrategy(new PayPalPayment("john.doe@email.com", "securepass")); + cart.checkout(); + + // More items + cart.addToCart(199.99); + + // Bank Transfer Payment + cart.setPaymentStrategy(new BankTransferPayment("12345678901", "123456789")); + cart.checkout(); + + // Final purchase + cart.addToCart(500.00); + + // Cryptocurrency Payment + cart.setPaymentStrategy(new CryptocurrencyPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "L4rK3xfFSS2zQS9p4mz2zAXQHEsLF5HQmh32xPBq8ZlA")); + cart.checkout(); + + // Test invalid payment methods + System.out.println("=== TESTING INVALID PAYMENT METHODS ==="); + cart.addToCart(25.00); + + // Invalid credit card + cart.setPaymentStrategy(new CreditCardPayment("123", "Invalid User", "12")); + cart.checkout(); + } +} diff --git a/src/content/design-pattern/code/strategy/solution.py b/src/content/design-pattern/code/strategy/solution.py new file mode 100644 index 0000000..e27788a --- /dev/null +++ b/src/content/design-pattern/code/strategy/solution.py @@ -0,0 +1,161 @@ +from abc import ABC, abstractmethod + +# Strategy interface +class PaymentStrategy(ABC): + @abstractmethod + def pay(self, amount: float) -> None: + pass + + @abstractmethod + def validate(self) -> bool: + pass + +# Concrete Strategies +class CreditCardPayment(PaymentStrategy): + def __init__(self, card_number: str, holder_name: str, cvv: str): + self.card_number = card_number + self.holder_name = holder_name + self.cvv = cvv + + def validate(self) -> bool: + return (self.card_number is not None and len(self.card_number) == 16 and + self.cvv is not None and len(self.cvv) == 3) + + def pay(self, amount: float) -> None: + if self.validate(): + print(f"Processing credit card payment of ${amount}") + print(f"Card: ****-****-****-{self.card_number[-4:]}") + print(f"Holder: {self.holder_name}") + print("Payment successful via Credit Card!") + else: + print("Invalid credit card details!") + +class PayPalPayment(PaymentStrategy): + def __init__(self, email: str, password: str): + self.email = email + self.password = password + + def validate(self) -> bool: + return (self.email is not None and "@" in self.email and + self.password is not None and len(self.password) >= 6) + + def pay(self, amount: float) -> None: + if self.validate(): + print("Connecting to PayPal...") + print(f"Processing PayPal payment of ${amount}") + print(f"Email: {self.email}") + print("Payment successful via PayPal!") + else: + print("Invalid PayPal credentials!") + +class BankTransferPayment(PaymentStrategy): + def __init__(self, account_number: str, routing_number: str): + self.account_number = account_number + self.routing_number = routing_number + + def validate(self) -> bool: + return (self.account_number is not None and len(self.account_number) >= 8 and + self.routing_number is not None and len(self.routing_number) == 9) + + def pay(self, amount: float) -> None: + if self.validate(): + print("Initiating bank transfer...") + print(f"Processing bank transfer of ${amount}") + print(f"Account: ****{self.account_number[-4:]}") + print("Payment successful via Bank Transfer!") + else: + print("Invalid bank account details!") + +class CryptocurrencyPayment(PaymentStrategy): + def __init__(self, wallet_address: str, private_key: str): + self.wallet_address = wallet_address + self.private_key = private_key + + def validate(self) -> bool: + return (self.wallet_address is not None and len(self.wallet_address) >= 26 and + self.private_key is not None and len(self.private_key) >= 32) + + def pay(self, amount: float) -> None: + if self.validate(): + print("Broadcasting cryptocurrency transaction...") + print(f"Processing crypto payment of ${amount}") + print(f"Wallet: {self.wallet_address[:6]}...") + print("Payment successful via Cryptocurrency!") + else: + print("Invalid cryptocurrency wallet details!") + +# Context class +class ShoppingCart: + def __init__(self): + self.payment_strategy = None + self.total_amount = 0.0 + + def set_payment_strategy(self, payment_strategy: PaymentStrategy) -> None: + self.payment_strategy = payment_strategy + + def add_to_cart(self, item_price: float) -> None: + self.total_amount += item_price + print(f"Item added. Current total: ${self.total_amount}") + + def checkout(self) -> None: + if self.payment_strategy is None: + print("Please select a payment method!") + return + + print("\n--- CHECKOUT PROCESS ---") + print(f"Total amount: ${self.total_amount}") + self.payment_strategy.pay(self.total_amount) + print("Checkout completed!\n") + self.total_amount = 0 # Reset cart after payment + + def get_total(self) -> float: + return self.total_amount + +def main(): + cart = ShoppingCart() + + # Add items to cart + print("=== SHOPPING SESSION ===") + cart.add_to_cart(29.99) + cart.add_to_cart(15.50) + cart.add_to_cart(75.25) + + # Try different payment strategies + print("\n=== TRYING DIFFERENT PAYMENT METHODS ===") + + # Credit Card Payment + cart.set_payment_strategy(CreditCardPayment("1234567890123456", "John Doe", "123")) + cart.checkout() + + # Add more items + cart.add_to_cart(99.99) + cart.add_to_cart(45.00) + + # PayPal Payment + cart.set_payment_strategy(PayPalPayment("john.doe@email.com", "securepass")) + cart.checkout() + + # More items + cart.add_to_cart(199.99) + + # Bank Transfer Payment + cart.set_payment_strategy(BankTransferPayment("12345678901", "123456789")) + cart.checkout() + + # Final purchase + cart.add_to_cart(500.00) + + # Cryptocurrency Payment + cart.set_payment_strategy(CryptocurrencyPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "L4rK3xfFSS2zQS9p4mz2zAXQHEsLF5HQmh32xPBq8ZlA")) + cart.checkout() + + # Test invalid payment methods + print("=== TESTING INVALID PAYMENT METHODS ===") + cart.add_to_cart(25.00) + + # Invalid credit card + cart.set_payment_strategy(CreditCardPayment("123", "Invalid User", "12")) + cart.checkout() + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/template-method/solution.cpp b/src/content/design-pattern/code/template-method/solution.cpp new file mode 100644 index 0000000..2826faf --- /dev/null +++ b/src/content/design-pattern/code/template-method/solution.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include + +// Abstract class defining the template method +class DataProcessor { +public: + virtual ~DataProcessor() = default; + + // Template method - defines the skeleton of the algorithm + void processData() { + std::cout << "=== STARTING DATA PROCESSING ===" << std::endl; + + // Step 1: Load data + loadData(); + + // Step 2: Validate data (optional hook) + if (shouldValidateData()) { + validateData(); + } + + // Step 3: Transform data + transformData(); + + // Step 4: Apply business rules (optional hook) + if (shouldApplyBusinessRules()) { + applyBusinessRules(); + } + + // Step 5: Save results + saveResults(); + + // Step 6: Cleanup (optional hook) + cleanup(); + + std::cout << "=== DATA PROCESSING COMPLETED ===\n" << std::endl; + } + +protected: + // Abstract methods - must be implemented by subclasses + virtual void loadData() = 0; + virtual void transformData() = 0; + virtual void saveResults() = 0; + + // Hook methods - can be overridden by subclasses (optional) + virtual bool shouldValidateData() { + return true; // Default behavior + } + + virtual void validateData() { + std::cout << "📋 Validating data format and integrity..." << std::endl; + } + + virtual bool shouldApplyBusinessRules() { + return false; // Default behavior - skip business rules + } + + virtual void applyBusinessRules() { + std::cout << "💼 Applying default business rules..." << std::endl; + } + + virtual void cleanup() { + std::cout << "🧹 Performing default cleanup..." << std::endl; + } +}; + +// Concrete implementation for CSV processing +class CSVDataProcessor : public DataProcessor { +protected: + void loadData() override { + std::cout << "📁 Loading data from CSV file..." << std::endl; + std::cout << " - Reading headers" << std::endl; + std::cout << " - Parsing rows" << std::endl; + std::cout << " - Loaded 1000 records" << std::endl; + } + + void transformData() override { + std::cout << "🔄 Transforming CSV data..." << std::endl; + std::cout << " - Converting strings to appropriate types" << std::endl; + std::cout << " - Normalizing date formats" << std::endl; + std::cout << " - Removing empty columns" << std::endl; + } + + void saveResults() override { + std::cout << "💾 Saving processed CSV data..." << std::endl; + std::cout << " - Writing to database table" << std::endl; + std::cout << " - Creating backup file" << std::endl; + } + + bool shouldApplyBusinessRules() override { + return true; // CSV data needs business rule validation + } + + void applyBusinessRules() override { + std::cout << "💼 Applying CSV-specific business rules..." << std::endl; + std::cout << " - Checking for duplicate customer IDs" << std::endl; + std::cout << " - Validating email formats" << std::endl; + std::cout << " - Flagging suspicious transactions" << std::endl; + } +}; + +// Concrete implementation for JSON processing +class JSONDataProcessor : public DataProcessor { +protected: + void loadData() override { + std::cout << "📁 Loading data from JSON API..." << std::endl; + std::cout << " - Making HTTP requests" << std::endl; + std::cout << " - Parsing JSON responses" << std::endl; + std::cout << " - Loaded 500 API objects" << std::endl; + } + + void transformData() override { + std::cout << "🔄 Transforming JSON data..." << std::endl; + std::cout << " - Flattening nested objects" << std::endl; + std::cout << " - Converting timestamps" << std::endl; + std::cout << " - Extracting relevant fields" << std::endl; + } + + void saveResults() override { + std::cout << "💾 Saving processed JSON data..." << std::endl; + std::cout << " - Writing to NoSQL database" << std::endl; + std::cout << " - Updating search index" << std::endl; + } + + bool shouldValidateData() override { + return false; // JSON from trusted API, skip validation + } + + void cleanup() override { + std::cout << "🧹 JSON-specific cleanup..." << std::endl; + std::cout << " - Clearing API cache" << std::endl; + std::cout << " - Logging API usage statistics" << std::endl; + } +}; + +// Concrete implementation for XML processing +class XMLDataProcessor : public DataProcessor { +protected: + void loadData() override { + std::cout << "📁 Loading data from XML file..." << std::endl; + std::cout << " - Parsing XML structure" << std::endl; + std::cout << " - Extracting elements and attributes" << std::endl; + std::cout << " - Loaded 750 XML nodes" << std::endl; + } + + void transformData() override { + std::cout << "🔄 Transforming XML data..." << std::endl; + std::cout << " - Converting XML to relational format" << std::endl; + std::cout << " - Handling CDATA sections" << std::endl; + std::cout << " - Resolving entity references" << std::endl; + } + + void saveResults() override { + std::cout << "💾 Saving processed XML data..." << std::endl; + std::cout << " - Writing to relational database" << std::endl; + std::cout << " - Generating summary report" << std::endl; + } + + void validateData() override { + std::cout << "📋 XML-specific validation..." << std::endl; + std::cout << " - Validating against XSD schema" << std::endl; + std::cout << " - Checking for malformed XML" << std::endl; + std::cout << " - Verifying required elements" << std::endl; + } + + bool shouldApplyBusinessRules() override { + return true; + } + + void applyBusinessRules() override { + std::cout << "💼 Applying XML-specific business rules..." << std::endl; + std::cout << " - Checking data completeness" << std::endl; + std::cout << " - Validating business logic constraints" << std::endl; + std::cout << " - Cross-referencing with master data" << std::endl; + } +}; + +int main() { + std::cout << "=== TEMPLATE METHOD PATTERN DEMO ===\n" << std::endl; + + // Create different data processors + std::vector> processors; + processors.push_back(std::make_unique()); + processors.push_back(std::make_unique()); + processors.push_back(std::make_unique()); + + // Process data using each processor + for (size_t i = 0; i < processors.size(); i++) { + std::cout << "PROCESSOR " << (i + 1) << ": " << typeid(*processors[i]).name() << std::endl; + processors[i]->processData(); + } + + std::cout << "=== TEMPLATE METHOD BENEFITS ===" << std::endl; + std::cout << "✓ Defines skeleton of algorithm in base class" << std::endl; + std::cout << "✓ Subclasses override specific steps without changing structure" << std::endl; + std::cout << "✓ Promotes code reuse and consistency" << std::endl; + std::cout << "✓ Hook methods provide flexibility for optional behavior" << std::endl; + std::cout << "✓ Follows Hollywood Principle: 'Don't call us, we'll call you'" << std::endl; + std::cout << "✓ Eliminates code duplication across similar algorithms" << std::endl; + + return 0; +} diff --git a/src/content/design-pattern/code/template-method/solution.java b/src/content/design-pattern/code/template-method/solution.java new file mode 100644 index 0000000..eeac28b --- /dev/null +++ b/src/content/design-pattern/code/template-method/solution.java @@ -0,0 +1,211 @@ +// Abstract class defining the template method +abstract class DataProcessor { + + // Template method - defines the skeleton of the algorithm + public final void processData() { + System.out.println("=== STARTING DATA PROCESSING ==="); + + // Step 1: Load data + loadData(); + + // Step 2: Validate data (optional hook) + if (shouldValidateData()) { + validateData(); + } + + // Step 3: Transform data + transformData(); + + // Step 4: Apply business rules (optional hook) + if (shouldApplyBusinessRules()) { + applyBusinessRules(); + } + + // Step 5: Save results + saveResults(); + + // Step 6: Cleanup (optional hook) + cleanup(); + + System.out.println("=== DATA PROCESSING COMPLETED ===\n"); + } + + // Abstract methods - must be implemented by subclasses + protected abstract void loadData(); + protected abstract void transformData(); + protected abstract void saveResults(); + + // Hook methods - can be overridden by subclasses (optional) + protected boolean shouldValidateData() { + return true; // Default behavior + } + + protected void validateData() { + System.out.println("📋 Validating data format and integrity..."); + } + + protected boolean shouldApplyBusinessRules() { + return false; // Default behavior - skip business rules + } + + protected void applyBusinessRules() { + System.out.println("💼 Applying default business rules..."); + } + + protected void cleanup() { + System.out.println("🧹 Performing default cleanup..."); + } +} + +// Concrete implementation for CSV processing +class CSVDataProcessor extends DataProcessor { + @Override + protected void loadData() { + System.out.println("📁 Loading data from CSV file..."); + System.out.println(" - Reading headers"); + System.out.println(" - Parsing rows"); + System.out.println(" - Loaded 1000 records"); + } + + @Override + protected void transformData() { + System.out.println("🔄 Transforming CSV data..."); + System.out.println(" - Converting strings to appropriate types"); + System.out.println(" - Normalizing date formats"); + System.out.println(" - Removing empty columns"); + } + + @Override + protected void saveResults() { + System.out.println("💾 Saving processed CSV data..."); + System.out.println(" - Writing to database table"); + System.out.println(" - Creating backup file"); + } + + @Override + protected boolean shouldApplyBusinessRules() { + return true; // CSV data needs business rule validation + } + + @Override + protected void applyBusinessRules() { + System.out.println("💼 Applying CSV-specific business rules..."); + System.out.println(" - Checking for duplicate customer IDs"); + System.out.println(" - Validating email formats"); + System.out.println(" - Flagging suspicious transactions"); + } +} + +// Concrete implementation for JSON processing +class JSONDataProcessor extends DataProcessor { + @Override + protected void loadData() { + System.out.println("📁 Loading data from JSON API..."); + System.out.println(" - Making HTTP requests"); + System.out.println(" - Parsing JSON responses"); + System.out.println(" - Loaded 500 API objects"); + } + + @Override + protected void transformData() { + System.out.println("🔄 Transforming JSON data..."); + System.out.println(" - Flattening nested objects"); + System.out.println(" - Converting timestamps"); + System.out.println(" - Extracting relevant fields"); + } + + @Override + protected void saveResults() { + System.out.println("💾 Saving processed JSON data..."); + System.out.println(" - Writing to NoSQL database"); + System.out.println(" - Updating search index"); + } + + @Override + protected boolean shouldValidateData() { + return false; // JSON from trusted API, skip validation + } + + @Override + protected void cleanup() { + System.out.println("🧹 JSON-specific cleanup..."); + System.out.println(" - Clearing API cache"); + System.out.println(" - Logging API usage statistics"); + } +} + +// Concrete implementation for XML processing +class XMLDataProcessor extends DataProcessor { + @Override + protected void loadData() { + System.out.println("📁 Loading data from XML file..."); + System.out.println(" - Parsing XML structure"); + System.out.println(" - Extracting elements and attributes"); + System.out.println(" - Loaded 750 XML nodes"); + } + + @Override + protected void transformData() { + System.out.println("🔄 Transforming XML data..."); + System.out.println(" - Converting XML to relational format"); + System.out.println(" - Handling CDATA sections"); + System.out.println(" - Resolving entity references"); + } + + @Override + protected void saveResults() { + System.out.println("💾 Saving processed XML data..."); + System.out.println(" - Writing to relational database"); + System.out.println(" - Generating summary report"); + } + + @Override + protected void validateData() { + System.out.println("📋 XML-specific validation..."); + System.out.println(" - Validating against XSD schema"); + System.out.println(" - Checking for malformed XML"); + System.out.println(" - Verifying required elements"); + } + + @Override + protected boolean shouldApplyBusinessRules() { + return true; + } + + @Override + protected void applyBusinessRules() { + System.out.println("💼 Applying XML-specific business rules..."); + System.out.println(" - Checking data completeness"); + System.out.println(" - Validating business logic constraints"); + System.out.println(" - Cross-referencing with master data"); + } +} + +// Demo class +public class TemplateMethodDemo { + public static void main(String[] args) { + System.out.println("=== TEMPLATE METHOD PATTERN DEMO ===\n"); + + // Create different data processors + DataProcessor[] processors = { + new CSVDataProcessor(), + new JSONDataProcessor(), + new XMLDataProcessor() + }; + + // Process data using each processor + for (int i = 0; i < processors.length; i++) { + System.out.println("PROCESSOR " + (i + 1) + ": " + + processors[i].getClass().getSimpleName()); + processors[i].processData(); + } + + System.out.println("=== TEMPLATE METHOD BENEFITS ==="); + System.out.println("✓ Defines skeleton of algorithm in base class"); + System.out.println("✓ Subclasses override specific steps without changing structure"); + System.out.println("✓ Promotes code reuse and consistency"); + System.out.println("✓ Hook methods provide flexibility for optional behavior"); + System.out.println("✓ Follows Hollywood Principle: 'Don't call us, we'll call you'"); + System.out.println("✓ Eliminates code duplication across similar algorithms"); + } +} diff --git a/src/content/design-pattern/code/template-method/solution.py b/src/content/design-pattern/code/template-method/solution.py new file mode 100644 index 0000000..cd9ea73 --- /dev/null +++ b/src/content/design-pattern/code/template-method/solution.py @@ -0,0 +1,175 @@ +from abc import ABC, abstractmethod +from typing import List + +# Abstract class defining the template method +class DataProcessor(ABC): + + def process_data(self) -> None: + """Template method - defines the skeleton of the algorithm""" + print("=== STARTING DATA PROCESSING ===") + + # Step 1: Load data + self.load_data() + + # Step 2: Validate data (optional hook) + if self.should_validate_data(): + self.validate_data() + + # Step 3: Transform data + self.transform_data() + + # Step 4: Apply business rules (optional hook) + if self.should_apply_business_rules(): + self.apply_business_rules() + + # Step 5: Save results + self.save_results() + + # Step 6: Cleanup (optional hook) + self.cleanup() + + print("=== DATA PROCESSING COMPLETED ===\n") + + # Abstract methods - must be implemented by subclasses + @abstractmethod + def load_data(self) -> None: + pass + + @abstractmethod + def transform_data(self) -> None: + pass + + @abstractmethod + def save_results(self) -> None: + pass + + # Hook methods - can be overridden by subclasses (optional) + def should_validate_data(self) -> bool: + return True # Default behavior + + def validate_data(self) -> None: + print("📋 Validating data format and integrity...") + + def should_apply_business_rules(self) -> bool: + return False # Default behavior - skip business rules + + def apply_business_rules(self) -> None: + print("💼 Applying default business rules...") + + def cleanup(self) -> None: + print("🧹 Performing default cleanup...") + +# Concrete implementation for CSV processing +class CSVDataProcessor(DataProcessor): + def load_data(self) -> None: + print("📁 Loading data from CSV file...") + print(" - Reading headers") + print(" - Parsing rows") + print(" - Loaded 1000 records") + + def transform_data(self) -> None: + print("🔄 Transforming CSV data...") + print(" - Converting strings to appropriate types") + print(" - Normalizing date formats") + print(" - Removing empty columns") + + def save_results(self) -> None: + print("💾 Saving processed CSV data...") + print(" - Writing to database table") + print(" - Creating backup file") + + def should_apply_business_rules(self) -> bool: + return True # CSV data needs business rule validation + + def apply_business_rules(self) -> None: + print("💼 Applying CSV-specific business rules...") + print(" - Checking for duplicate customer IDs") + print(" - Validating email formats") + print(" - Flagging suspicious transactions") + +# Concrete implementation for JSON processing +class JSONDataProcessor(DataProcessor): + def load_data(self) -> None: + print("📁 Loading data from JSON API...") + print(" - Making HTTP requests") + print(" - Parsing JSON responses") + print(" - Loaded 500 API objects") + + def transform_data(self) -> None: + print("🔄 Transforming JSON data...") + print(" - Flattening nested objects") + print(" - Converting timestamps") + print(" - Extracting relevant fields") + + def save_results(self) -> None: + print("💾 Saving processed JSON data...") + print(" - Writing to NoSQL database") + print(" - Updating search index") + + def should_validate_data(self) -> bool: + return False # JSON from trusted API, skip validation + + def cleanup(self) -> None: + print("🧹 JSON-specific cleanup...") + print(" - Clearing API cache") + print(" - Logging API usage statistics") + +# Concrete implementation for XML processing +class XMLDataProcessor(DataProcessor): + def load_data(self) -> None: + print("📁 Loading data from XML file...") + print(" - Parsing XML structure") + print(" - Extracting elements and attributes") + print(" - Loaded 750 XML nodes") + + def transform_data(self) -> None: + print("🔄 Transforming XML data...") + print(" - Converting XML to relational format") + print(" - Handling CDATA sections") + print(" - Resolving entity references") + + def save_results(self) -> None: + print("💾 Saving processed XML data...") + print(" - Writing to relational database") + print(" - Generating summary report") + + def validate_data(self) -> None: + print("📋 XML-specific validation...") + print(" - Validating against XSD schema") + print(" - Checking for malformed XML") + print(" - Verifying required elements") + + def should_apply_business_rules(self) -> bool: + return True + + def apply_business_rules(self) -> None: + print("💼 Applying XML-specific business rules...") + print(" - Checking data completeness") + print(" - Validating business logic constraints") + print(" - Cross-referencing with master data") + +def main(): + print("=== TEMPLATE METHOD PATTERN DEMO ===\n") + + # Create different data processors + processors: List[DataProcessor] = [ + CSVDataProcessor(), + JSONDataProcessor(), + XMLDataProcessor() + ] + + # Process data using each processor + for i, processor in enumerate(processors, 1): + print(f"PROCESSOR {i}: {processor.__class__.__name__}") + processor.process_data() + + print("=== TEMPLATE METHOD BENEFITS ===") + print("✓ Defines skeleton of algorithm in base class") + print("✓ Subclasses override specific steps without changing structure") + print("✓ Promotes code reuse and consistency") + print("✓ Hook methods provide flexibility for optional behavior") + print("✓ Follows Hollywood Principle: 'Don't call us, we'll call you'") + print("✓ Eliminates code duplication across similar algorithms") + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/code/visitor/solution.cpp b/src/content/design-pattern/code/visitor/solution.cpp new file mode 100644 index 0000000..f26f0bf --- /dev/null +++ b/src/content/design-pattern/code/visitor/solution.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +// Forward declarations +class PDFDocument; +class WordDocument; +class ExcelDocument; + +// Visitor interface +class DocumentVisitor { +public: + virtual ~DocumentVisitor() = default; + virtual void visit(const PDFDocument& pdf) = 0; + virtual void visit(const WordDocument& word) = 0; + virtual void visit(const ExcelDocument& excel) = 0; +}; + +// Element interface +class Document { +public: + virtual ~Document() = default; + virtual void accept(DocumentVisitor& visitor) const = 0; + virtual std::string getName() const = 0; + virtual int getSize() const = 0; +}; + +// Concrete Elements +class PDFDocument : public Document { +private: + std::string name; + int pages; + int size; + +public: + PDFDocument(const std::string& name, int pages, int size) + : name(name), pages(pages), size(size) {} + + void accept(DocumentVisitor& visitor) const override { + visitor.visit(*this); + } + + std::string getName() const override { return name; } + int getSize() const override { return size; } + int getPages() const { return pages; } +}; + +class WordDocument : public Document { +private: + std::string name; + int wordCount; + int size; + +public: + WordDocument(const std::string& name, int wordCount, int size) + : name(name), wordCount(wordCount), size(size) {} + + void accept(DocumentVisitor& visitor) const override { + visitor.visit(*this); + } + + std::string getName() const override { return name; } + int getSize() const override { return size; } + int getWordCount() const { return wordCount; } +}; + +class ExcelDocument : public Document { +private: + std::string name; + int sheetCount; + int size; + +public: + ExcelDocument(const std::string& name, int sheetCount, int size) + : name(name), sheetCount(sheetCount), size(size) {} + + void accept(DocumentVisitor& visitor) const override { + visitor.visit(*this); + } + + std::string getName() const override { return name; } + int getSize() const override { return size; } + int getSheetCount() const { return sheetCount; } +}; + +// Concrete Visitors +class DocumentReportVisitor : public DocumentVisitor { +private: + int totalSize = 0; + int documentCount = 0; + +public: + void visit(const PDFDocument& pdf) override { + std::cout << "PDF Report: " << pdf.getName() << " - " << pdf.getPages() + << " pages, " << pdf.getSize() << " KB" << std::endl; + totalSize += pdf.getSize(); + documentCount++; + } + + void visit(const WordDocument& word) override { + std::cout << "Word Report: " << word.getName() << " - " << word.getWordCount() + << " words, " << word.getSize() << " KB" << std::endl; + totalSize += word.getSize(); + documentCount++; + } + + void visit(const ExcelDocument& excel) override { + std::cout << "Excel Report: " << excel.getName() << " - " << excel.getSheetCount() + << " sheets, " << excel.getSize() << " KB" << std::endl; + totalSize += excel.getSize(); + documentCount++; + } + + void printSummary() const { + std::cout << "\n=== SUMMARY ===" << std::endl; + std::cout << "Total Documents: " << documentCount << std::endl; + std::cout << "Total Size: " << totalSize << " KB" << std::endl; + } +}; + +class DocumentCompressionVisitor : public DocumentVisitor { +public: + void visit(const PDFDocument& pdf) override { + std::cout << "Compressing PDF: " << pdf.getName() << " - Compression ratio: 15%" << std::endl; + } + + void visit(const WordDocument& word) override { + std::cout << "Compressing Word: " << word.getName() << " - Compression ratio: 25%" << std::endl; + } + + void visit(const ExcelDocument& excel) override { + std::cout << "Compressing Excel: " << excel.getName() << " - Compression ratio: 30%" << std::endl; + } +}; + +int main() { + // Create document collection + std::vector> documents; + documents.push_back(std::make_unique("Annual Report.pdf", 50, 1200)); + documents.push_back(std::make_unique("Meeting Minutes.docx", 2500, 800)); + documents.push_back(std::make_unique("Budget 2024.xlsx", 12, 1500)); + documents.push_back(std::make_unique("User Manual.pdf", 100, 2000)); + documents.push_back(std::make_unique("Project Proposal.docx", 5000, 1100)); + + std::cout << "=== GENERATING REPORTS ===" << std::endl; + DocumentReportVisitor reportVisitor; + for (const auto& doc : documents) { + doc->accept(reportVisitor); + } + reportVisitor.printSummary(); + + std::cout << "\n=== COMPRESSING DOCUMENTS ===" << std::endl; + DocumentCompressionVisitor compressionVisitor; + for (const auto& doc : documents) { + doc->accept(compressionVisitor); + } + + return 0; +} diff --git a/src/content/design-pattern/code/visitor/solution.java b/src/content/design-pattern/code/visitor/solution.java new file mode 100644 index 0000000..032e993 --- /dev/null +++ b/src/content/design-pattern/code/visitor/solution.java @@ -0,0 +1,165 @@ +// Visitor interface +interface DocumentVisitor { + void visit(PDFDocument pdf); + void visit(WordDocument word); + void visit(ExcelDocument excel); +} + +// Element interface +interface Document { + void accept(DocumentVisitor visitor); + String getName(); + int getSize(); +} + +// Concrete Elements +class PDFDocument implements Document { + private String name; + private int pages; + private int size; + + public PDFDocument(String name, int pages, int size) { + this.name = name; + this.pages = pages; + this.size = size; + } + + @Override + public void accept(DocumentVisitor visitor) { + visitor.visit(this); + } + + @Override + public String getName() { return name; } + + @Override + public int getSize() { return size; } + + public int getPages() { return pages; } +} + +class WordDocument implements Document { + private String name; + private int wordCount; + private int size; + + public WordDocument(String name, int wordCount, int size) { + this.name = name; + this.wordCount = wordCount; + this.size = size; + } + + @Override + public void accept(DocumentVisitor visitor) { + visitor.visit(this); + } + + @Override + public String getName() { return name; } + + @Override + public int getSize() { return size; } + + public int getWordCount() { return wordCount; } +} + +class ExcelDocument implements Document { + private String name; + private int sheetCount; + private int size; + + public ExcelDocument(String name, int sheetCount, int size) { + this.name = name; + this.sheetCount = sheetCount; + this.size = size; + } + + @Override + public void accept(DocumentVisitor visitor) { + visitor.visit(this); + } + + @Override + public String getName() { return name; } + + @Override + public int getSize() { return size; } + + public int getSheetCount() { return sheetCount; } +} + +// Concrete Visitors +class DocumentReportVisitor implements DocumentVisitor { + private int totalSize = 0; + private int documentCount = 0; + + @Override + public void visit(PDFDocument pdf) { + System.out.println("PDF Report: " + pdf.getName() + " - " + pdf.getPages() + " pages, " + pdf.getSize() + " KB"); + totalSize += pdf.getSize(); + documentCount++; + } + + @Override + public void visit(WordDocument word) { + System.out.println("Word Report: " + word.getName() + " - " + word.getWordCount() + " words, " + word.getSize() + " KB"); + totalSize += word.getSize(); + documentCount++; + } + + @Override + public void visit(ExcelDocument excel) { + System.out.println("Excel Report: " + excel.getName() + " - " + excel.getSheetCount() + " sheets, " + excel.getSize() + " KB"); + totalSize += excel.getSize(); + documentCount++; + } + + public void printSummary() { + System.out.println("\n=== SUMMARY ==="); + System.out.println("Total Documents: " + documentCount); + System.out.println("Total Size: " + totalSize + " KB"); + } +} + +class DocumentCompressionVisitor implements DocumentVisitor { + @Override + public void visit(PDFDocument pdf) { + System.out.println("Compressing PDF: " + pdf.getName() + " - Compression ratio: 15%"); + } + + @Override + public void visit(WordDocument word) { + System.out.println("Compressing Word: " + word.getName() + " - Compression ratio: 25%"); + } + + @Override + public void visit(ExcelDocument excel) { + System.out.println("Compressing Excel: " + excel.getName() + " - Compression ratio: 30%"); + } +} + +public class VisitorPatternDemo { + public static void main(String[] args) { + // Create document collection + Document[] documents = { + new PDFDocument("Annual Report.pdf", 50, 1200), + new WordDocument("Meeting Minutes.docx", 2500, 800), + new ExcelDocument("Budget 2024.xlsx", 12, 1500), + new PDFDocument("User Manual.pdf", 100, 2000), + new WordDocument("Project Proposal.docx", 5000, 1100) + }; + + System.out.println("=== GENERATING REPORTS ==="); + DocumentReportVisitor reportVisitor = new DocumentReportVisitor(); + for (Document doc : documents) { + doc.accept(reportVisitor); + } + reportVisitor.printSummary(); + + System.out.println("\n=== COMPRESSING DOCUMENTS ==="); + DocumentCompressionVisitor compressionVisitor = new DocumentCompressionVisitor(); + for (Document doc : documents) { + doc.accept(compressionVisitor); + } + } +} diff --git a/src/content/design-pattern/code/visitor/solution.py b/src/content/design-pattern/code/visitor/solution.py new file mode 100644 index 0000000..235c81b --- /dev/null +++ b/src/content/design-pattern/code/visitor/solution.py @@ -0,0 +1,148 @@ +from abc import ABC, abstractmethod +from typing import List + +# Forward references for type hints +from __future__ import annotations + +# Visitor interface +class DocumentVisitor(ABC): + @abstractmethod + def visit_pdf(self, pdf: PDFDocument) -> None: + pass + + @abstractmethod + def visit_word(self, word: WordDocument) -> None: + pass + + @abstractmethod + def visit_excel(self, excel: ExcelDocument) -> None: + pass + +# Element interface +class Document(ABC): + @abstractmethod + def accept(self, visitor: DocumentVisitor) -> None: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + @abstractmethod + def get_size(self) -> int: + pass + +# Concrete Elements +class PDFDocument(Document): + def __init__(self, name: str, pages: int, size: int): + self._name = name + self._pages = pages + self._size = size + + def accept(self, visitor: DocumentVisitor) -> None: + visitor.visit_pdf(self) + + def get_name(self) -> str: + return self._name + + def get_size(self) -> int: + return self._size + + def get_pages(self) -> int: + return self._pages + +class WordDocument(Document): + def __init__(self, name: str, word_count: int, size: int): + self._name = name + self._word_count = word_count + self._size = size + + def accept(self, visitor: DocumentVisitor) -> None: + visitor.visit_word(self) + + def get_name(self) -> str: + return self._name + + def get_size(self) -> int: + return self._size + + def get_word_count(self) -> int: + return self._word_count + +class ExcelDocument(Document): + def __init__(self, name: str, sheet_count: int, size: int): + self._name = name + self._sheet_count = sheet_count + self._size = size + + def accept(self, visitor: DocumentVisitor) -> None: + visitor.visit_excel(self) + + def get_name(self) -> str: + return self._name + + def get_size(self) -> int: + return self._size + + def get_sheet_count(self) -> int: + return self._sheet_count + +# Concrete Visitors +class DocumentReportVisitor(DocumentVisitor): + def __init__(self): + self._total_size = 0 + self._document_count = 0 + + def visit_pdf(self, pdf: PDFDocument) -> None: + print(f"PDF Report: {pdf.get_name()} - {pdf.get_pages()} pages, {pdf.get_size()} KB") + self._total_size += pdf.get_size() + self._document_count += 1 + + def visit_word(self, word: WordDocument) -> None: + print(f"Word Report: {word.get_name()} - {word.get_word_count()} words, {word.get_size()} KB") + self._total_size += word.get_size() + self._document_count += 1 + + def visit_excel(self, excel: ExcelDocument) -> None: + print(f"Excel Report: {excel.get_name()} - {excel.get_sheet_count()} sheets, {excel.get_size()} KB") + self._total_size += excel.get_size() + self._document_count += 1 + + def print_summary(self) -> None: + print("\n=== SUMMARY ===") + print(f"Total Documents: {self._document_count}") + print(f"Total Size: {self._total_size} KB") + +class DocumentCompressionVisitor(DocumentVisitor): + def visit_pdf(self, pdf: PDFDocument) -> None: + print(f"Compressing PDF: {pdf.get_name()} - Compression ratio: 15%") + + def visit_word(self, word: WordDocument) -> None: + print(f"Compressing Word: {word.get_name()} - Compression ratio: 25%") + + def visit_excel(self, excel: ExcelDocument) -> None: + print(f"Compressing Excel: {excel.get_name()} - Compression ratio: 30%") + +def main(): + # Create document collection + documents: List[Document] = [ + PDFDocument("Annual Report.pdf", 50, 1200), + WordDocument("Meeting Minutes.docx", 2500, 800), + ExcelDocument("Budget 2024.xlsx", 12, 1500), + PDFDocument("User Manual.pdf", 100, 2000), + WordDocument("Project Proposal.docx", 5000, 1100) + ] + + print("=== GENERATING REPORTS ===") + report_visitor = DocumentReportVisitor() + for doc in documents: + doc.accept(report_visitor) + report_visitor.print_summary() + + print("\n=== COMPRESSING DOCUMENTS ===") + compression_visitor = DocumentCompressionVisitor() + for doc in documents: + doc.accept(compression_visitor) + +if __name__ == "__main__": + main() diff --git a/src/content/design-pattern/posts/abstract-factory.mdx b/src/content/design-pattern/posts/abstract-factory.mdx new file mode 100644 index 0000000..682a4cd --- /dev/null +++ b/src/content/design-pattern/posts/abstract-factory.mdx @@ -0,0 +1,68 @@ +--- +layout: post +title: Abstract Factory Design Pattern +difficulty: hard +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: system-design/abstract-factory-pattern +refactoring: abstract-factory +sourcemaking: abstract_factory +wikipedia: Abstract_factory_pattern +scaler: design-patterns/abstract-factory-design-pattern/ +--- + +## Intent +- Provide an interface for creating families of related or dependent objects without specifying their concrete classes +- A hierarchy that encapsulates many possible platforms and the construction of a suite of products +- The "new" operator considered harmful when creating objects of concrete classes + +## Problem + +Imagine you're creating a furniture shop simulator. Your code consists of classes that represent a family of related products, say: Chair + Sofa + CoffeeTable. You need several variants of this family. For example, you have products Chair + Sofa + CoffeeTable available in these variants: Modern, Victorian, ArtDeco. + +You need a way to create individual furniture objects so that they match other objects of the same family. Customers get quite mad when they receive non-matching furniture. Also, you don't want to change existing code when adding new products or families of products to the program. + +## Structure + +The Abstract Factory pattern suggests explicitly declaring interfaces for each distinct product of the product family. Then you can make all variants of products follow those interfaces. + + + +The next step is to declare the Abstract Factory—an interface with a list of creation methods for all products that are part of the product family. These methods must return abstract product types represented by the interfaces we extracted previously. + + + +## Example + +Think of the Abstract Factory as a factory that produces other factories. Each factory type corresponds to a certain product variety. For instance, a Modern Furniture Factory can create Modern Chairs, Modern Sofas, and Modern Coffee Tables. A Victorian Furniture Factory produces Victorian-style furniture, and so on. + +The client code can work with any concrete factory, as long as it communicates with its products via abstract interfaces. This allows the client code to be independent of the actual type of products it gets from a factory object. + +## Implementation Checklist + +1. Map out a matrix of distinct product types versus variants of these products +2. Declare abstract product interfaces for all product types. Then make all concrete product classes implement these interfaces +3. Declare the abstract factory interface with a set of creation methods for all abstract products +4. Implement a set of concrete factory classes, one for each product variant +5. Create factory initialization code somewhere in the app. It should instantiate one of the concrete factory classes, depending on the application configuration or the current environment +6. Scan through the code and find all direct calls to product constructors. Replace them with calls to the appropriate creation method on the factory object + +## Pros and Cons + +**Pros:** +- Ensures that the products you get from a factory are compatible with each other +- Avoids tight coupling between concrete products and client code +- Single Responsibility Principle. You can extract the product creation code into one place +- Open/Closed Principle. You can introduce new variants of products without breaking existing client code + +**Cons:** +- Code may become more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern + +## Rules of Thumb + +- Abstract Factory can serve as an alternative to Facade when you only want to hide the way the subsystem objects are created from the client code +- You can use Abstract Factory along with Bridge. This pairing is useful when some abstractions defined by Bridge can only work with specific implementations +- Abstract Factories, Builders and Prototypes can all be implemented as Singletons +- Abstract Factory is often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes +- Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method diff --git a/src/content/design-pattern/posts/adapter.mdx b/src/content/design-pattern/posts/adapter.mdx index 48e1669..1214ba7 100644 --- a/src/content/design-pattern/posts/adapter.mdx +++ b/src/content/design-pattern/posts/adapter.mdx @@ -25,11 +25,11 @@ An "off the shelf" component offers compelling functionality that you would like Below, a legacy Rectangle component's `display()` method expects to receive "x, y, w, h" parameters. But the client wants to pass "upper left x and y" and "lower right x and y". This incongruity can be reconciled by adding an additional level of indirection – i.e. an Adapter object. - + The Adapter could also be thought of as a "wrapper". - + ## Example @@ -39,7 +39,7 @@ Typical drive sizes in the United States are 1/2" and 1/4". Obviously, a 1/2" drive ratchet will not fit into a 1/4" drive socket unless an adapter is used. A 1/2" to 1/4" adapter has a 1/2" female connection to fit on the 1/2" drive ratchet, and a 1/4" male connection to fit in the 1/4" drive socket. - + ## Checklist diff --git a/src/content/design-pattern/posts/bridge.mdx b/src/content/design-pattern/posts/bridge.mdx index a417013..2eef65c 100644 --- a/src/content/design-pattern/posts/bridge.mdx +++ b/src/content/design-pattern/posts/bridge.mdx @@ -5,7 +5,65 @@ difficulty: hard tags: [structural] langs: [java, py, cpp] companies: [] +gfg: system-design/bridge-design-pattern refactoring: bridge sourcemaking: bridge +wikipedia: Bridge_pattern +scaler: design-patterns/bridge-design-pattern/ --- +## Intent + +- Decouple an abstraction from its implementation so that the two can vary independently. +- Publish interface in an inheritance hierarchy, and bury implementation in its own inheritance hierarchy. +- Beyond encapsulation, to insulation + +## Problem + +"Hardening of the software arteries" has occurred by using subclassing of an abstract base class to provide alternative implementations. This locks in compile-time binding between interface and implementation. The abstraction and implementation cannot be independently extended or composed. + +Consider the domain of "thread scheduling". There are two types of thread schedulers, and two types of operating systems or "platforms". Given this approach to specialization, we have to define a class for each permutation of these two dimensions. + + + +What if we had three kinds of thread schedulers, and four kinds of platforms? The number of classes we would have to define is the product of the number of scheduling schemes and the number of platforms. + + + +## Solution + +The Bridge design pattern proposes refactoring this exponentially explosive inheritance hierarchy into two orthogonal hierarchies – one for platform-independent abstractions, and the other for platform-dependent implementations. + + + +Decompose the component's interface and implementation into orthogonal class hierarchies. The interface class contains a pointer to the abstract implementation class. This pointer is initialized with an instance of a concrete implementation class, but all subsequent interaction from the interface class to the implementation class is limited to the abstraction maintained in the implementation base class. + +## Structure + +The Client doesn't want to deal with platform-dependent details. The Bridge pattern encapsulates this complexity behind an abstraction "wrapper". + + + +## Example + +The Bridge pattern decouples an abstraction from its implementation, so that the two can vary independently. A household switch controlling lights, ceiling fans, etc. is an example of the Bridge. The purpose of the switch is to turn a device on or off. The actual switch can be implemented as a pull chain, simple two position switch, or a variety of dimmer switches. + + + +## Checklist + +1. Decide if two orthogonal dimensions exist in the domain. These independent concepts could be: abstraction/platform, or domain/infrastructure, or front-end/back-end, or interface/implementation. +2. Design the separation of concerns: what does the client want, and what do the platforms provide. +3. Design a platform-oriented interface that is minimal, necessary, and sufficient. Its goal is to decouple the abstraction from the platform. +4. Define a derived class of that interface for each platform. +5. Create the abstraction base class that "has a" platform object and delegates the platform-oriented functionality to it. +6. Define specializations of the abstraction class if desired. + +## Rule of thumb + +- Adapter makes things work after they're designed; Bridge makes them work before they are. +- Bridge is designed up-front to let the abstraction and the implementation vary independently. Adapter is retrofitted to make unrelated classes work together. +- State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom. They differ in intent - that is, they solve different problems. +- The structure of State and Bridge are identical (except that Bridge admits hierarchies of envelope classes, whereas State allows only one). The two patterns use the same structure to solve different problems: State allows an object's behavior to change along with its state, while Bridge's intent is to decouple an abstraction from its implementation so that the two can vary independently. +- If interface classes delegate the creation of their implementation classes (instead of creating/coupling themselves directly), then the design usually uses the Abstract Factory pattern to create the implementation objects. + diff --git a/src/content/design-pattern/posts/builder.mdx b/src/content/design-pattern/posts/builder.mdx new file mode 100644 index 0000000..a5453c7 --- /dev/null +++ b/src/content/design-pattern/posts/builder.mdx @@ -0,0 +1,69 @@ +--- +layout: post +title: Builder Design Pattern +difficulty: medium +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: system-design/builder-design-pattern +refactoring: builder +sourcemaking: builder +wikipedia: Builder_pattern +scaler: design-patterns/builder-design-pattern/ +--- + +## Intent +- Separate the construction of a complex object from its representation so that the same construction process can create different representations +- Parse a complex representation, create one of several targets +- Construct a complex object step by step. The final step returns the object + +## Problem + +Imagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. Such initialization code is usually buried inside a monstrous constructor with lots of parameters. Or even worse: scattered all over the client code. + +Consider how to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a bigger, brighter house, with a backyard and other goodies (like a heating system, plumbing, and electrical wiring)? + +The simplest solution is to extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually you'll end up with a considerable number of subclasses. + +## Structure + +The Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called builders. The pattern organizes object construction into a set of steps. To create an object, you execute a series of these steps on a builder object. + + + +You can go further and extract a series of calls to the builder steps you use to construct a product into a separate class called director. The director class defines the order in which to execute the building steps. + + + +## Example + +You can think of the Builder pattern as a specialized foreman who knows how to build particular types of things. The same foreman (Director) can work with different construction crews (Builders) to create different products. For instance, the same set of instructions can be used to build a stone house, a wooden house, or even a manual describing how to build the house. + +The key difference is that while similar construction steps are used, the materials and techniques differ, resulting in completely different products. + +## Implementation Checklist + +1. Make sure that you can clearly define the common construction steps for building all available product representations +2. Declare these steps in the base builder interface +3. Create a concrete builder class for each of the product representations and implement their construction steps +4. Think about creating a director class. It may encapsulate various ways to construct a product using the same builder object +5. The client code creates both the builder and the director objects. Before construction starts, the client must pass a builder object to the director +6. The construction result can be obtained directly from the director only if all products follow the same interface + +## Pros and Cons + +**Pros:** +- Construct objects step-by-step, defer construction steps or run steps recursively +- Reuse the same construction code when building various representations of products +- Single Responsibility Principle. Isolate complex construction code from the business logic of the product + +**Cons:** +- Overall complexity of the code increases since the pattern requires creating multiple new classes + +## Rules of Thumb + +- Many designs start out using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated) +- Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects +- You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively +- You can combine Builder with Bridge: the director class plays the role of the abstraction, while different builders act as implementations +- Abstract Factories, Builders and Prototypes can all be implemented as Singletons diff --git a/src/content/design-pattern/posts/chain-of-responsibility.mdx b/src/content/design-pattern/posts/chain-of-responsibility.mdx new file mode 100644 index 0000000..c85f6e0 --- /dev/null +++ b/src/content/design-pattern/posts/chain-of-responsibility.mdx @@ -0,0 +1,72 @@ +--- +layout: post +title: Chain of Responsibility Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/chain-responsibility-design-pattern +refactoring: chain-of-responsibility +sourcemaking: chain_of_responsibility +wikipedia: Chain-of-responsibility_pattern +scaler: design-patterns/chain-of-responsibility-design-pattern/ +--- + +## Intent +- Pass requests along a chain of handlers, with each handler deciding whether to process the request or pass it to the next handler +- Decouple the sender of a request from its receivers by giving multiple objects a chance to handle the request +- Launch-and-leave request handling with a processing pipeline that contains many possible handlers + +## Problem + +Imagine you're building an online ordering system where incoming requests need to undergo sequential validation checks - authentication, data validation, rate limiting, and caching. If these checks are implemented as a monolithic block of code, several issues arise: + +The code becomes bloated and messy as more validation steps are added. It's difficult to modify individual checks without affecting others, leading to maintenance nightmares. Components become tightly coupled and hard to reuse in different contexts where only a subset of checks might be needed. + +## Solution + +The Chain of Responsibility pattern transforms each validation step into a separate handler object, linking them together to form a chain. When a request arrives, it travels through the chain until a handler can process it or the chain ends. + + + +Each handler in the chain follows a simple protocol: examine the request, decide whether to handle it, and either process it or pass it to the next handler. This creates a flexible pipeline where handlers can be easily added, removed, or reordered. + + + +## Example + +Think of a customer service call center. When you call with an issue, your call first goes to a front-desk agent who handles basic inquiries. If they can't resolve your issue, they escalate it to a technical specialist. If the technical specialist can't help, the call goes to a senior engineer, and so on. + +Each person in the chain has specific expertise and handles requests they're qualified for, while passing more complex issues up the chain. No single person needs to know how to handle every possible customer issue. + + + +## Implementation Checklist + +1. **Define Handler Interface**: Create a common interface with methods for handling requests and setting the next handler +2. **Create Base Handler**: Implement boilerplate code for storing references to the next handler and default request forwarding +3. **Implement Concrete Handlers**: Create specific handler classes that contain the actual processing logic +4. **Build the Chain**: Link handlers together by setting the next handler for each one in the sequence +5. **Client Integration**: Send requests to the first handler in the chain and let the pattern do its work +6. **Add Safety Net**: Include a default handler at the end to catch unprocessed requests + +## Pros and Cons + +**Pros:** +- **Decoupling**: Separates request senders from receivers, reducing dependencies +- **Flexibility**: Handlers can be added, removed, or reordered at runtime +- **Single Responsibility**: Each handler focuses on a specific processing step +- **Open/Closed Principle**: New handlers can be introduced without changing existing code + +**Cons:** +- **No Handling Guarantee**: Requests might pass through the entire chain without being handled +- **Performance Overhead**: Long chains can impact performance as requests traverse multiple handlers +- **Debugging Complexity**: Tracing request flow through complex chains can be challenging + +## Rules of Thumb + +- Chain of Responsibility, Command, Mediator, and Observer all address sender-receiver coupling but with different approaches +- Often combined with Composite pattern when building tree structures with request propagation +- Can use Command objects as requests to enable more complex request structures and queuing +- Similar to Decorator in structure but different in intent - CoR can break the processing flow while Decorator cannot +- Consider when you have multiple objects that can handle a request but the specific handler isn't known beforehand diff --git a/src/content/design-pattern/posts/command.mdx b/src/content/design-pattern/posts/command.mdx new file mode 100644 index 0000000..dac8e33 --- /dev/null +++ b/src/content/design-pattern/posts/command.mdx @@ -0,0 +1,72 @@ +--- +layout: post +title: Command Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/command-pattern +refactoring: command +sourcemaking: command +wikipedia: Command_pattern +scaler: design-patterns/command-design-pattern/ +--- + +## Intent +- Encapsulate a request as an object, allowing you to parameterize clients with different requests and queue or log operations +- Turn requests into stand-alone objects that contain all information about the request +- Support undoable operations by storing commands along with their execution state + +## Problem + +Consider building a text editor with multiple ways to trigger the same operation - toolbar buttons, context menus, and keyboard shortcuts. A naive approach would embed the business logic directly in UI elements, creating tight coupling between interface and functionality. + +This approach leads to massive code duplication when the same operation needs to be accessible from multiple places. It becomes nearly impossible to implement features like undo/redo, macro recording, or operation queuing because the logic is scattered across different UI components. + +## Solution + +The Command pattern extracts requests into separate command objects that act as intermediaries between UI elements and business logic. Each command encapsulates all the information needed to perform an operation, including the receiver object and method parameters. + + + +Commands implement a common interface with an execute() method, making them interchangeable. This allows the same UI element to work with different commands, and the same command to be triggered from multiple sources. + + + +## Example + +Imagine ordering food at a restaurant. You (the client) don't go directly to the kitchen (receiver) to request your meal. Instead, you give your order to a waiter (invoker) who writes it down on an order slip (command object). This slip contains all the necessary information and can be queued, prioritized, or even cancelled. + +The waiter doesn't need to know how to cook - they just need to know how to deliver the order slip to the kitchen. The kitchen can process orders in any sequence, and the restaurant can implement features like order tracking or cancellation. + + + +## Implementation Checklist + +1. **Define Command Interface**: Create an interface with execute() and optionally undo() methods +2. **Create Concrete Commands**: Implement command classes that encapsulate specific operations with their receivers +3. **Identify Invokers**: Modify sender classes to store and work with command objects instead of calling receivers directly +4. **Connect Components**: Client code creates command objects, configures them with receivers, and associates them with invokers +5. **Add History Support**: Implement command storage for undo/redo functionality +6. **Consider Macro Commands**: Create composite commands that execute multiple operations in sequence + +## Pros and Cons + +**Pros:** +- **Single Responsibility**: Decouples invokers from receivers, separating request initiation from execution +- **Open/Closed**: New commands can be added without changing existing invoker or receiver code +- **Undo/Redo Support**: Commands can store state to enable operation reversal +- **Deferred Execution**: Operations can be queued, scheduled, or executed remotely +- **Macro Support**: Complex operations can be built by combining simple commands + +**Cons:** +- **Code Complexity**: Introduces an additional layer of objects, increasing overall complexity +- **Memory Overhead**: Storing command objects, especially for undo/redo, can consume significant memory + +## Rules of Thumb + +- Command, Chain of Responsibility, Mediator, and Observer all help decouple senders and receivers of requests +- Commands can be used as parameters for Chain of Responsibility handlers to create more flexible processing pipelines +- Often combined with Memento pattern for implementing undo functionality - Commands perform operations while Mementos save state +- Strategy and Command are similar but Strategy defines how to do something while Command defines what to do +- Use when you need to parameterize objects with operations, queue operations, or support undo functionality diff --git a/src/content/design-pattern/posts/composite.mdx b/src/content/design-pattern/posts/composite.mdx index 7d08230..bf4257f 100644 --- a/src/content/design-pattern/posts/composite.mdx +++ b/src/content/design-pattern/posts/composite.mdx @@ -5,7 +5,59 @@ difficulty: hard tags: [structural] langs: [java, py, cpp] companies: [] +gfg: system-design/composite-design-pattern refactoring: composite sourcemaking: composite +wikipedia: Composite_pattern +scaler: design-patterns/composite-design-pattern/ --- +## Intent + +- Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. +- Recursive composition +- "Directories contain entries, each of which could be a directory." +- 1-to-many "has a" up the "is a" hierarchy + +## Problem + +Application needs to manipulate a hierarchical collection of "primitive" and "composite" objects. Processing of a primitive object is handled one way, and processing of a composite object is handled differently. Having to query the "type" of each object before attempting to process it is not desirable. + +## Solution + +Define an abstract base class (Component) that specifies the behavior that needs to be exercised uniformly across all primitive and composite objects. Subclass the Primitive and Composite classes off of the Component class. Each Composite object "couples" itself only to the abstract type Component as it manages its "children". + +Use this pattern whenever you have "composites that contain components, each of which could be a composite". + +## Structure + +Composites that contain Components, each of which could be a Composite. + + + +Menus that contain menu items, each of which could be a menu. Row-column GUI layout managers that contain widgets, each of which could be a row-column GUI layout manager. Directories that contain files, each of which could be a directory. Containers that contain Elements, each of which could be a Container. + +## Example + +The Composite composes objects into tree structures and lets clients treat individual objects and compositions uniformly. Although the example is abstract, arithmetic expressions are Composites. An arithmetic expression consists of an operand, an operator (+ - * /), and another operand. The operand can be a number, or another arithmetic expression. Thus, 2 + 3 and (2 + 3) + (4 * 6) are both valid expressions. + + + +## Checklist + +1. Ensure that your problem is about representing "whole-part" hierarchical relationships. +2. Consider the heuristic, "Containers that contain containees, each of which could be a container." For example, "Assemblies that contain components, each of which could be an assembly." Divide your domain concepts into container classes, and containee classes. +3. Create a "lowest common denominator" interface that makes your containers and containees interchangeable. It should specify the behavior that needs to be exercised uniformly across all containee and container objects. +4. All container and containee classes declare an "is a" relationship to the interface. +5. All container classes declare a one-to-many "has a" relationship to the interface. +6. Container classes leverage polymorphism to delegate to their containee objects. +7. Child management methods [e.g. addChild(), removeChild()] should normally be defined in the Composite class. Unfortunately, the desire to treat Leaf and Composite objects uniformly may require that these methods be promoted to the abstract Component class. + +## Rule of thumb + +- Composite and Decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open-ended number of objects. +- Composite can be traversed with Iterator. Visitor can apply an operation over a Composite. Composite could use Chain of Responsibility to let components access global properties through their parent. +- Composite can let you compose a Mediator out of smaller pieces through recursive composition. +- Decorator is designed to let you add responsibilities to objects without subclassing. Composite's focus is not on embellishment but on representation. These intents are distinct but complementary. Consequently, Composite and Decorator are often used in concert. +- Flyweight is often combined with Composite to implement shared leaf nodes. + diff --git a/src/content/design-pattern/posts/decorator.mdx b/src/content/design-pattern/posts/decorator.mdx new file mode 100644 index 0000000..75363cf --- /dev/null +++ b/src/content/design-pattern/posts/decorator.mdx @@ -0,0 +1,73 @@ +--- +layout: post +title: Decorator Design Pattern +difficulty: medium +tags: [structural] +langs: [java, py, cpp] +companies: [] +gfg: system-design/decorator-pattern +refactoring: decorator +sourcemaking: decorator +wikipedia: Decorator_pattern +scaler: design-patterns/decorator-design-pattern/ +--- + +## Intent +- Attach new behaviors to objects dynamically by placing these objects inside special wrapper objects that contain the behaviors. +- Provide a flexible alternative to subclassing for extending functionality. +- Allow responsibilities to be added and removed from an object at runtime. + +## Problem +You need to extend an object's behavior dynamically but creating subclasses for every possible combination would lead to a combinatorial explosion of classes. For example, a notification system might need to send notifications via email, SMS, Facebook, and various combinations of these channels. Creating separate subclasses for every combination (EmailNotifier, SMSNotifier, FacebookNotifier, EmailAndSMSNotifier, etc.) becomes unmanageable. + +Another issue is that inheritance is static - you can't change the behavior of an existing object at runtime by altering its class. + +## Structure + +The key insight is to aggregate objects with the target behavior in decorators, rather than trying to integrate all variations into a single class. + + + +The Decorator pattern suggests creating a set of decorator classes that are used to wrap concrete components. Each decorator has a field for storing a reference to a wrapped object. + + + +## Example + +The Decorator pattern is like wearing clothes. You start with a basic outfit (the core object), then you can add layers like a sweater, jacket, or raincoat (decorators). Each piece of clothing modifies your appearance or capabilities (behavior) without changing who you fundamentally are. You can put on and take off layers as needed, and the order might matter (sweater before jacket). + +Another analogy is data processing: you might have a basic data source that reads from a file, then wrap it with compression, encryption, and logging decorators. Each decorator adds its functionality while maintaining the same interface. + + + +## Checklist + +1. Ensure that your business domain can be represented as a primary component with optional layers over it. +2. Figure out what methods are common to both the primary component and the optional layers. Create a component interface and declare those methods there. +3. Create a concrete component class and define the base behavior in it. +4. Create a base decorator class. It should have a field for storing a reference to a wrapped object. The field should be declared with the component interface type to allow linking to concrete components as well as decorators. The base decorator must delegate all work to the wrapped object. +5. Make sure all classes implement the component interface. +6. Create concrete decorators by extending them from the base decorator. A concrete decorator must execute its behavior before or after the call to the parent method (which delegates to the wrapped object). +7. The client code must be responsible for creating decorators and composing them in the way the client needs. + +## Pros and Cons + +**Pros:** +- You can extend an object's behavior without making a new subclass +- You can add or remove responsibilities from an object at runtime +- You can combine several behaviors by wrapping an object into multiple decorators +- Single Responsibility Principle: You can divide a monolithic class that implements many possible variants of behavior into several smaller classes + +**Cons:** +- It's hard to remove a specific wrapper from the wrappers stack +- It's hard to implement a decorator in such a way that its behavior doesn't depend on the order in the wrapper stack +- The initial configuration code of layers might look pretty ugly + +## Rule of thumb + +- Adapter changes the interface of an existing object, while Decorator enhances an object without changing its interface. In addition, Decorator supports recursive composition, which isn't possible with pure Adapters. +- Adapter provides a different interface to the wrapped object, Proxy provides the same interface, and Decorator provides an enhanced interface. +- Chain of Responsibility and Decorator have very similar structures. Both patterns rely on recursive composition to pass the execution through a series of handlers. However, there are several crucial differences. +- Composite and Decorator have similar structure diagrams since both rely on recursive composition to organize an open-ended number of objects. +- Designs that make heavy use of Composite and Decorator can often benefit from using Prototype. Applying the pattern lets you clone complex structures instead of re-constructing them from scratch. +- Decorator lets you change the skin of an object, while Strategy lets you change the guts. diff --git a/src/content/design-pattern/posts/facade.mdx b/src/content/design-pattern/posts/facade.mdx new file mode 100644 index 0000000..c019b11 --- /dev/null +++ b/src/content/design-pattern/posts/facade.mdx @@ -0,0 +1,70 @@ +--- +layout: post +title: Facade Design Pattern +difficulty: easy +tags: [structural] +langs: [java, py, cpp] +companies: [] +gfg: system-design/facade-design-pattern-introduction +refactoring: facade +sourcemaking: facade +wikipedia: Facade_pattern +scaler: design-patterns/facade-design-pattern/ +--- + +## Intent +- Provide a unified, simplified interface to a set of interfaces in a subsystem. +- Define a higher-level interface that makes the subsystem easier to use. +- Wrap a complex subsystem with a simpler interface. + +## Problem +Imagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, you'd need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on. + +As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain. + +## Structure + +The Facade pattern suggests that you wrap a complex subsystem with a simpler interface. The facade hides most of the complexity by having the facade communicate with subsystem classes on your behalf. + + + +The Facade provides convenient access to a particular part of the subsystem's functionality. It knows where to direct the client's request and how to operate all the moving parts. + + + +## Example + +When you call a shop to place a phone order, an operator is your facade to all services and departments of the shop. The operator provides you with a simple voice interface to the ordering system, payment gateways, and various delivery services. + + + +Similarly, a video conversion facade might provide a simple method like `convertVideo(filename, format)` while internally coordinating between video/audio codecs, filters, and various format libraries. + +## Checklist + +1. Check whether it's possible to provide a simpler interface than what an existing subsystem already provides. You're on the right track if this interface makes the client code independent from many of the subsystem's classes. +2. Declare and implement this interface in a new facade class. The facade should redirect the calls from the client code to appropriate objects of the subsystem. The facade should be responsible for initializing the subsystem and managing its lifecycle unless the client code already does this. +3. To get the full benefit from the pattern, make all the client code communicate with the subsystem only via the facade. Now the client code is protected from any changes in the subsystem code. For example, when a subsystem gets upgraded to a new version, you will only need to modify the code in the facade. +4. If the facade becomes too big, consider extracting part of its behavior to a new, refined facade class. + +## Pros and Cons + +**Pros:** +- You can isolate your code from the complexity of a subsystem +- Reduces coupling between the client and subsystem +- Provides a simple interface to a complex subsystem +- Can be used to layer a system and define entry points to each level + +**Cons:** +- A facade can become a god object coupled to all classes of an app +- May limit access to advanced features of the subsystem +- Adds an additional layer of indirection + +## Rule of thumb + +- Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects. +- Abstract Factory can serve as an alternative to Facade when you only want to hide the way the subsystem objects are created from the client code. +- Flyweight shows how to make lots of little objects, whereas Facade shows how to make a single object represent an entire subsystem. +- Facade is similar to Proxy in that both buffer a complex entity and initialize it on their own. Unlike Facade, Proxy has the same interface as its service object, which makes them interchangeable. +- Facade classes can often be transformed into Singletons since a single facade object is sufficient in most cases. +- Mediator and Facade have similar jobs: they try to organize collaboration between lots of tightly coupled classes. Facade defines a simplified interface to a subsystem of objects, but it doesn't introduce any new functionality. The subsystem itself is unaware of the facade. Objects within the subsystem can communicate directly. Mediator centralizes communication between components of the system. The components only know about the mediator object and don't communicate directly. diff --git a/src/content/design-pattern/posts/factory-method.mdx b/src/content/design-pattern/posts/factory-method.mdx new file mode 100644 index 0000000..ab67da8 --- /dev/null +++ b/src/content/design-pattern/posts/factory-method.mdx @@ -0,0 +1,65 @@ +--- +layout: post +title: Factory Method Design Pattern +difficulty: medium +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: system-design/factory-method-for-designing-pattern +refactoring: factory-method +sourcemaking: factory_method +wikipedia: Factory_method_pattern +scaler: design-patterns/factory-method-design-pattern/ +--- + +## Intent +- Define an interface for creating an object, but let subclasses decide which class to instantiate +- Factory Method lets a class defer instantiation to subclasses +- Provide a way to encapsulate a group of individual factories with a common theme + +## Problem + +Imagine you're creating a logistics management application. The first version of your app can only handle transportation by trucks, so the bulk of your code lives inside the `Truck` class. After a while, your app becomes pretty popular. Each day you receive dozens of requests from sea transportation companies to incorporate sea logistics into the app. + +Adding a new transportation class to the program isn't that simple if the rest of the code is already coupled to existing classes. Adding `Ship` classes would require making changes to the entire codebase. Worse, if later you decide to add another type of transportation to the app, you will probably need to make all of these changes again. + +## Structure + +The Factory Method pattern suggests that you replace direct object construction calls (using the `new` operator) with calls to a special factory method. Don't worry: the objects are still created via the `new` operator, but it's being called from within the factory method. Objects returned by a factory method are often referred to as products. + + + +The Factory Method pattern is based on inheritance: it relies on a class hierarchy and delegates the object creation to the subclasses. + + + +## Example + +The Factory Method pattern allows creating transport objects without specifying the exact class of object that will be created. For instance, a logistics application might use trucks for road transport and ships for sea transport. The client code doesn't need to know whether it's working with a truck or ship - it just calls the deliver method on the transport object. + +This is similar to how different assembly lines in a factory produce different products but use similar processes and tooling. The factory method determines which "assembly line" (concrete creator) to use based on the specific requirements. + +## Implementation Checklist + +1. Make all products follow the same interface. This interface should declare methods that make sense in every product. +2. Add an empty factory method inside the creator class. The return type of the method should match the common product interface. +3. In the creator's code find all references to product constructors. One by one, replace them with calls to the factory method, while extracting the product creation code into the factory method. +4. Create a set of creator subclasses for each type of product listed in the factory method. Override the factory method in the subclasses and extract the appropriate bits of construction code from the base method. +5. If there are too many product types and it doesn't make sense to create subclasses for all of them, you can reuse the control parameter from the base class in subclasses. + +## Pros and Cons + +**Pros:** +- Avoids tight coupling between the creator and the concrete products +- Single Responsibility Principle. You can move the product creation code into one place in the program +- Open/Closed Principle. You can introduce new types of products into the program without breaking existing client code + +**Cons:** +- Code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern + +## Rules of Thumb + +- Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method +- Factory Method is based on inheritance but doesn't require an initialization step. Prototype is based on a copy or clone step. As such, Factory Method requires subclassing, but Prototype doesn't +- Factory Methods are usually called within Template Methods +- Abstract Factory is often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes diff --git a/src/content/design-pattern/posts/flyweight.mdx b/src/content/design-pattern/posts/flyweight.mdx new file mode 100644 index 0000000..750bda6 --- /dev/null +++ b/src/content/design-pattern/posts/flyweight.mdx @@ -0,0 +1,76 @@ +--- +layout: post +title: Flyweight Design Pattern +difficulty: hard +tags: [structural] +langs: [java, py, cpp] +companies: [] +gfg: system-design/flyweight-design-pattern +refactoring: flyweight +sourcemaking: flyweight +wikipedia: Flyweight_pattern +scaler: design-patterns/flyweight-design-pattern/ +--- + +## Intent +- Use sharing to support large numbers of fine-grained objects efficiently. +- Minimize memory usage when you need to support vast quantities of similar objects. +- Separate intrinsic (shareable) state from extrinsic (context-specific) state. + +## Problem +To have some fun after long hours of work, you decided to create a simple video game where players move around a map and shoot at each other. You chose to implement a realistic particle system and make it a distinctive feature of the game. Vast quantities of bullets, missiles, and shrapnel from explosions should fly all over the map and deliver a thrilling experience to the player. + +Upon its completion, you pushed the last commit, built the game and sent it to your friend for a test drive. Although the game was running flawlessly on your machine, your friend wasn't able to play for long. On his computer, the game kept crashing after a few minutes of gameplay. After spending several hours digging through debug logs, you discovered that the game was crashing due to an insufficient amount of RAM. It turned out that your friend's computer was less powerful than yours, and that's why the problem emerged so quickly on his machine. + +The actual problem was related to your particle system. Each particle, such as a bullet, missile or piece of shrapnel was represented by a separate object containing plenty of data. At some point, when the carnage on a player's screen reached its climax, newly created particles no longer fit into the remaining RAM, so the program crashed. + +## Structure + +The Flyweight pattern suggests that you stop storing the extrinsic state inside the object. Instead, you should pass this state to the specific methods which rely on it. Only the intrinsic state stays within the object, letting you reuse it in different contexts. + + + +The intrinsic state is the data required by a flyweight to function. The extrinsic state is the data upon which flyweight methods act. + + + +## Example + +Think of the Flyweight pattern as the way letters work in a word processor. Each letter of the alphabet (A, B, C, etc.) is like a flyweight - the letter's shape, font style, and size are intrinsic properties that can be shared. However, each letter's position on the page and color are extrinsic properties unique to each usage. + +Instead of storing the font data with each character instance in your document, the word processor reuses a single "A" flyweight object for all "A" characters, just passing different position coordinates when rendering each one. + + + +Another example is a forest simulation where you have millions of tree objects. The tree type (oak, pine, birch), color, and sprite are intrinsic and can be shared among trees of the same type. The position, age, and health are extrinsic and unique to each tree instance. + +## Checklist + +1. Divide fields of a class that will become a flyweight into two parts: + - Intrinsic state: the fields that contain unchanging data duplicated across many objects + - Extrinsic state: the fields that contain contextual data unique to each object +2. Leave the fields that represent the intrinsic state in the class, but make sure they're immutable. They should take their initial values only inside the constructor. +3. Go over methods that use fields of the extrinsic state. For each field used in the method, introduce a new parameter and use it instead of the field. +4. Optionally, create a factory class to manage the pool of flyweights. It should check for an existing flyweight before creating a new one. Once the factory is in place, clients must only request flyweights through it. They must describe the desired flyweight by passing its intrinsic state to the factory. +5. The client must store or calculate values of the extrinsic state (context) to be able to call methods of flyweight objects. For the sake of convenience, the extrinsic state along with the flyweight-referencing field may be moved to a separate context class. + +## Pros and Cons + +**Pros:** +- You can save lots of RAM, assuming your program has tons of similar objects +- Centralizes state management for similar objects +- Reduces memory footprint significantly in scenarios with massive object creation + +**Cons:** +- You might be trading RAM over CPU cycles if some of the context data needs to be recalculated each time somebody calls a flyweight method +- The code becomes much more complicated. New team members will always be wondering why the state of an entity was separated in such a way +- Flyweight objects must be immutable, which can be limiting in some scenarios + +## Rule of thumb + +- You can implement shared leaf nodes of the Composite tree as Flyweights to save some RAM. +- Flyweight shows how to make lots of little objects, whereas Facade shows how to make a single object that represents an entire subsystem. +- Flyweight would resemble Singleton if you somehow managed to reduce all shared states of the objects to just one flyweight object. But there are two fundamental differences between these patterns: + - There should be only one Singleton instance, whereas a Flyweight class can have multiple instances with different intrinsic states. + - The Singleton object can be mutable. Flyweight objects are immutable. +- State and Strategy objects are often implemented as Flyweights. diff --git a/src/content/design-pattern/posts/interpreter.mdx b/src/content/design-pattern/posts/interpreter.mdx new file mode 100644 index 0000000..c7d8781 --- /dev/null +++ b/src/content/design-pattern/posts/interpreter.mdx @@ -0,0 +1,75 @@ +--- +layout: post +title: Interpreter Design Pattern +difficulty: hard +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/interpreter-design-pattern +refactoring: interpreter +sourcemaking: interpreter +wikipedia: Interpreter_pattern +scaler: design-patterns/interpreter-design-pattern/ +--- + +## Intent +- Define a representation for a language's grammar and an interpreter that uses this representation to interpret sentences in that language +- Map a domain to a language, then map that language to a grammar, and finally represent that grammar with an object-oriented design +- Build an interpretation engine for a well-defined domain language + +## Problem + +When you encounter a class of problems that are well-defined and well-understood within a specific domain, and this domain could be characterized by a "language," then problems within that domain could be easily solved by an "interpretation engine." + +The typical scenario involves creating a simple language to express domain-specific operations. For example, you might need to evaluate mathematical expressions, process configuration rules, or interpret command sequences. Without the Interpreter pattern, you'd end up with complex conditional logic scattered throughout your code. + +## Structure + +The Interpreter pattern models the domain using a recursive grammar. Each rule in the grammar is represented as a class. The pattern leverages the recursive traversal of the Composite pattern to interpret the "sentences" it processes. + + + +The pattern consists of: +- **AbstractExpression**: Declares an abstract `interpret()` method +- **TerminalExpression**: Implements terminal symbols in the grammar +- **NonterminalExpression**: Implements composite expressions that contain other expressions +- **Context**: Contains information that is global to the interpreter + +## Example + +Think of musicians interpreting musical notation. The musical score represents the "language" of music, the notation (pitch, duration, etc.) defines the grammar, and musicians act as the "interpreters," reading the score and reproducing the intended sounds. + +Another example is a calculator that can interpret and evaluate arithmetic expressions like "2 + 3 * 4". Each number is a terminal expression, while operators like addition and multiplication are non-terminal expressions that combine other expressions. + +## Checklist + +1. Define the grammar for your domain language +2. Map each production in the grammar to a class +3. Organize these classes using the Composite pattern +4. Define an `interpret()` method in the abstract expression class +5. Create terminal expression classes for basic elements +6. Create non-terminal expression classes for complex rules +7. Create a Context class to hold global state +8. Build the abstract syntax tree and invoke interpretation + +## Pros and Cons + +**Pros:** +- Easy to change and extend the grammar +- Implementing the grammar is straightforward +- Complex grammars can be represented in a clear object hierarchy +- Adding new ways to interpret expressions is easy + +**Cons:** +- Complex grammars are hard to maintain +- Performance can be poor for complex expressions +- Not suitable for complex parsing (use parser generators instead) +- Can lead to a large number of classes + +## Rules of Thumb + +- Nearly all uses of Composite can involve Interpreter, but Interpreter is specifically for when you want to view the class hierarchy as defining a language +- Interpreter can use State for parsing contexts +- The abstract syntax tree is a Composite, making Iterator and Visitor applicable +- Terminal symbols can be shared using Flyweight +- Consider if a "little language" provides a justifiable return on investment for your problem diff --git a/src/content/design-pattern/posts/iterator.mdx b/src/content/design-pattern/posts/iterator.mdx new file mode 100644 index 0000000..813fe6b --- /dev/null +++ b/src/content/design-pattern/posts/iterator.mdx @@ -0,0 +1,72 @@ +--- +layout: post +title: Iterator Design Pattern +difficulty: easy +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/iterator-pattern +refactoring: iterator +sourcemaking: iterator +wikipedia: Iterator_pattern +scaler: design-patterns/iterator-design-pattern/ +--- + +## Intent +- Provide a way to access elements of an aggregate object sequentially without exposing its underlying representation +- Support multiple simultaneous traversals of aggregate objects +- Provide a uniform interface for traversing different aggregate structures + +## Problem + +Collections are fundamental data structures, but they can be implemented in vastly different ways - arrays, lists, trees, graphs, and other complex structures. Each implementation might require different traversal approaches, and some collections like trees can support multiple traversal algorithms (depth-first, breadth-first). + +Adding all possible traversal methods directly to collection classes clutters their primary responsibility of data storage. It also makes client code tightly coupled to specific collection implementations, reducing flexibility when switching between different data structures. + +## Solution + +The Iterator pattern extracts traversal behavior into separate iterator objects. Each iterator encapsulates the traversal algorithm and maintains its current position, allowing multiple iterators to traverse the same collection independently. + + + +All iterators implement a common interface with methods like next() and hasNext(), providing a uniform way to traverse any collection regardless of its internal structure. This decouples client code from collection implementation details. + + + +## Example + +Think of exploring a city like Rome. You have several options: wandering aimlessly (direct access), using a smartphone app for guided tours (one type of iterator), or hiring different local guides for food tours, historical tours, or art tours (different iterator implementations). + +Each guide (iterator) knows how to navigate the city (collection) and can take you through attractions in a specific order, but you don't need to know the city's layout or navigation details. You can even have multiple guides for different types of experiences. + + + +## Implementation Checklist + +1. **Declare Iterator Interface**: Define methods like getNext(), hasMore(), and optionally reset() or previous() +2. **Declare Collection Interface**: Add a factory method that returns new iterator instances +3. **Implement Concrete Iterators**: Create iterator classes for each collection type, managing traversal state and position +4. **Implement Concrete Collections**: Add the iterator factory method that returns appropriate iterator instances +5. **Client Integration**: Replace direct collection access with iterator-based traversal +6. **Consider Multiple Iterator Types**: Support different traversal algorithms for the same collection + +## Pros and Cons + +**Pros:** +- **Single Responsibility**: Separates collection storage from traversal algorithms +- **Open/Closed**: New collections and iteration algorithms can be added independently +- **Parallel Iteration**: Multiple iterators can traverse the same collection simultaneously +- **Deferred Iteration**: Iteration can be paused, resumed, or cancelled as needed + +**Cons:** +- **Overkill for Simple Collections**: May add unnecessary complexity for straightforward data structures +- **Performance Overhead**: Iterator abstraction may be slower than direct access for simple cases + +## Rules of Thumb + +- Iterator is often used with Composite to traverse tree structures +- Factory Method can help collection subclasses return different iterator types +- Memento can work with Iterator to capture and restore iteration state +- Visitor can be used alongside Iterator to perform operations on collection elements during traversal +- Most modern programming languages provide built-in iterator support, but understanding the pattern helps when implementing custom collections +- Consider using Iterator when collections have complex internal structures or need multiple traversal methods diff --git a/src/content/design-pattern/posts/mediator.mdx b/src/content/design-pattern/posts/mediator.mdx new file mode 100644 index 0000000..3ae223f --- /dev/null +++ b/src/content/design-pattern/posts/mediator.mdx @@ -0,0 +1,72 @@ +--- +layout: post +title: Mediator Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/mediator-design-pattern +refactoring: mediator +sourcemaking: mediator +wikipedia: Mediator_pattern +scaler: design-patterns/mediator-design-pattern/ +--- + +## Intent +- Define how a set of objects interact with each other by centralizing complex communications and control logic +- Reduce chaotic dependencies between communicating objects by forcing them to collaborate indirectly through a mediator +- Encapsulate how multiple objects interact and make the interaction reusable + +## Problem + +In complex systems, objects often need to interact with multiple other objects, creating a web of interdependencies. Consider a user interface with various form elements - checkboxes, text fields, buttons, and dropdowns. Each element might need to enable/disable others, validate input, or trigger actions based on state changes. + +When objects communicate directly, they become tightly coupled and hard to reuse. A checkbox that directly controls three text fields can't be easily moved to a different dialog. The system becomes fragile because changes to one component can cascade through many others. + +## Solution + +The Mediator pattern introduces a mediator object that centralizes communication between components. Instead of objects referencing each other directly, they only know about the mediator. When an object needs to communicate, it sends a message to the mediator, which then determines how to handle it. + + + +This approach transforms a many-to-many relationship into a one-to-many relationship, with the mediator at the center. Components become more reusable because they're no longer coupled to specific sets of collaborators. + + + +## Example + +An air traffic control tower serves as a perfect example of the Mediator pattern. Pilots don't communicate directly with each other to coordinate takeoffs, landings, and flight paths - this would create chaos with dozens of aircraft trying to negotiate simultaneously. + +Instead, all communication goes through the control tower. The tower has a complete view of air traffic and makes coordinated decisions about flight patterns, landing sequences, and safety protocols. Pilots only need to know how to communicate with the tower, not with every other aircraft. + + + +## Implementation Checklist + +1. **Identify Tightly Coupled Classes**: Find classes that have too many direct dependencies on each other +2. **Design Mediator Interface**: Create an interface with methods for receiving notifications from components +3. **Implement Concrete Mediator**: Build a class that coordinates interactions and holds references to all components +4. **Modify Components**: Replace direct component-to-component calls with calls to the mediator +5. **Establish Mediator References**: Ensure each component has a reference to its mediator (usually passed in constructor) +6. **Centralize Interaction Logic**: Move complex interaction rules from components into the mediator + +## Pros and Cons + +**Pros:** +- **Single Responsibility**: Extracts communication logic into a dedicated class +- **Open/Closed**: New mediators can change component behavior without modifying the components +- **Reduced Coupling**: Components don't need to know about each other directly +- **Improved Reusability**: Components can be reused with different mediators in different contexts + +**Cons:** +- **God Object Risk**: Mediators can become overly complex and accumulate too much responsibility +- **Complexity Shift**: While simplifying component relationships, the mediator itself can become complex + +## Rules of Thumb + +- Mediator and Observer are often confused - Mediator centralizes communication while Observer creates dynamic subscriptions +- Facade provides a simplified interface to a subsystem, while Mediator centralizes communication between tightly coupled objects +- Chain of Responsibility, Command, Mediator, and Observer all address sender-receiver coupling differently +- Mediator can be implemented using Observer pattern, with the mediator as publisher and components as subscribers +- Consider Mediator when you have a group of tightly coupled classes that are hard to maintain or reuse +- Use when you want to reuse components in different contexts by changing only the mediator diff --git a/src/content/design-pattern/posts/memento.mdx b/src/content/design-pattern/posts/memento.mdx new file mode 100644 index 0000000..f4d7b46 --- /dev/null +++ b/src/content/design-pattern/posts/memento.mdx @@ -0,0 +1,72 @@ +--- +layout: post +title: Memento Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/memento-design-pattern +refactoring: memento +sourcemaking: memento +wikipedia: Memento_pattern +scaler: design-patterns/memento-design-pattern/ +--- + +## Intent +- Capture and externalize an object's internal state without violating encapsulation, so the object can be restored to this state later +- Provide the ability to restore an object to its previous state (undo functionality) +- Save snapshots of an object's state without exposing its internal structure + +## Problem + +Implementing undo functionality requires saving object states before modifications, but this creates a dilemma. Making fields public breaks encapsulation, while requiring objects to expose their private state through getters violates the principle of information hiding. + +Creating a separate snapshot class that mirrors the object's structure makes the system fragile - any change to the original object requires updating the snapshot class. This tight coupling makes maintenance difficult and error-prone. + +## Solution + +The Memento pattern delegates the responsibility of creating state snapshots to the object that owns the state. The originator creates a memento object containing its current state, but the memento's contents are only accessible to the originator that created it. + + + +Other objects (caretakers) can store and manage mementos but cannot examine or modify their contents. This preserves encapsulation while enabling state restoration functionality. + + + +## Example + +Consider a camera with manual settings for aperture, shutter speed, and ISO. When shooting in challenging conditions, you might want to experiment with different settings but quickly return to a known good configuration. + +The camera can save its current settings (memento) to a memory slot (caretaker). You can create multiple snapshots for different scenarios - portrait settings, landscape settings, low-light settings. Later, you can restore any saved configuration without manually adjusting each setting. + + + +## Implementation Checklist + +1. **Identify the Originator**: Determine which class needs state saving and restoration capabilities +2. **Create Memento Class**: Design a class to hold the originator's state, making it immutable where possible +3. **Implement Snapshot Creation**: Add a method to the originator that creates and returns a memento with current state +4. **Implement State Restoration**: Add a method to the originator that accepts a memento and restores state from it +5. **Design Caretaker**: Create a class responsible for storing and managing mementos (like a history manager) +6. **Control Access**: Ensure only the originator can access memento contents, typically through nested classes or interfaces + +## Pros and Cons + +**Pros:** +- **Encapsulation Preservation**: Saves and restores state without breaking object encapsulation +- **Simplified Originator**: Object doesn't need to manage its history or multiple versions +- **Caretaker Independence**: History management is separate from business logic + +**Cons:** +- **Memory Consumption**: Storing many mementos can consume significant memory +- **Lifecycle Management**: Caretakers must manage memento lifecycles to prevent memory leaks +- **Language Limitations**: Some languages make it difficult to guarantee memento contents remain unmodified + +## Rules of Thumb + +- Often used with Command pattern to implement undo/redo - Commands execute operations while Mementos save state +- Can work with Iterator to save iteration state for later restoration +- Prototype pattern can be a simpler alternative when object state is straightforward +- Consider using when you need undo functionality, checkpoints, or transaction rollback capabilities +- Memento preserves encapsulation where a simple state copy would violate it +- Essential for implementing editors, games with save states, or any system requiring state rollback diff --git a/src/content/design-pattern/posts/null-object.mdx b/src/content/design-pattern/posts/null-object.mdx new file mode 100644 index 0000000..cb85ea3 --- /dev/null +++ b/src/content/design-pattern/posts/null-object.mdx @@ -0,0 +1,74 @@ +--- +layout: post +title: Null Object Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/null-object-design-pattern +refactoring: null-object +sourcemaking: null_object +wikipedia: Null_object_pattern +scaler: design-patterns/null-object-design-pattern/ +--- + +## Intent +- Encapsulate the absence of an object by providing a substitutable alternative that offers suitable default "do nothing" behavior +- Abstract away the handling of null references from the client +- Eliminate repetitive null checks in client code + +## Problem + +When an object reference might be null, the typical response is to do nothing or use a default value. This often leads to repetitive null checks scattered throughout the client code, making it cluttered and error-prone. + +Consider a logging system where sometimes no logger is available. Without the Null Object pattern, you'd need to check for null before every log operation: `if (logger != null) logger.log(message)`. This becomes tedious and error-prone when repeated throughout the codebase. + +## Structure + +The Null Object pattern provides a way to treat the absence of an object as a valid, albeit inactive, object. + + + +Key components: +- **Client**: Uses the collaborator through a common interface +- **AbstractObject**: Declares the interface for the collaborator +- **RealObject**: Provides useful, expected behavior +- **NullObject**: Provides identical interface but with "do nothing" implementations + +## Example + +Consider a graphics application where shapes can have different fill strategies. Sometimes a shape should have no fill at all. Instead of checking for null fill strategies everywhere, you create a `NoFillStrategy` that implements the same interface but does nothing when asked to fill a shape. + +Another example is a notification system. Instead of checking if a notification service is available, you can have a `NullNotificationService` that silently ignores all notification requests, allowing the code to continue without interruption. + +## Checklist + +1. Identify situations where you're checking for null references +2. Define an abstract interface for the collaborator +3. Create a concrete class that provides real functionality +4. Create a null object class that implements the same interface with "do nothing" behavior +5. Replace null checks with null object instances +6. Consider implementing the null object as a Singleton if it has no state + +## Pros and Cons + +**Pros:** +- Eliminates need for null checks in client code +- Simplifies client code and makes it more readable +- Reduces risk of null pointer exceptions +- Provides consistent behavior regardless of object availability +- Follows polymorphism principles + +**Cons:** +- Can make debugging harder as errors fail silently +- May mask real problems if null objects hide important error conditions +- Can lead to unexpected behavior if clients expect exceptions for null cases +- Adds extra classes to the system + +## Rules of Thumb + +- A Null Object does not transform into a Real Object - if transformation is needed, consider State or Proxy patterns instead +- Null Objects are often implemented as Singletons since they typically have no state +- If different clients expect different "do nothing" behaviors, you may need multiple Null Object classes +- The Null Object can be a special case of Strategy pattern (a strategy that does nothing) +- Use with Visitor pattern to allow safe traversal of hierarchies without explicit null checks diff --git a/src/content/design-pattern/posts/object-pool.mdx b/src/content/design-pattern/posts/object-pool.mdx new file mode 100644 index 0000000..41abbc0 --- /dev/null +++ b/src/content/design-pattern/posts/object-pool.mdx @@ -0,0 +1,79 @@ +--- +layout: post +title: Object Pool Design Pattern +difficulty: medium +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: java/object-pool-design-pattern +refactoring: object-pool +sourcemaking: object_pool +wikipedia: Object_pool_pattern +scaler: design-patterns/object-pool-design-pattern/ +--- + +## Intent +- Improve performance by managing a collection of reusable objects +- Avoid expensive object creation and destruction when objects are frequently needed +- Control the number of instances of a particular class + +## Problem + +Creating new objects can be computationally expensive or time-consuming, especially for objects that require significant initialization (like database connections, thread objects, or graphics resources). If an application frequently creates and destroys such objects, this can lead to performance bottlenecks and memory overhead. + +Additionally, in some scenarios, you need to limit the total number of objects of a particular type that can exist at any given time, such as limiting database connections to prevent overwhelming the database server. + +## Structure + +The Object Pool pattern creates a "pool" of pre-instantiated objects that can be reused instead of creating new objects each time. + + + +Key components: +- **Reusable**: Objects that can be used by clients and then returned to the pool +- **Client**: Objects that use the reusable objects +- **ReusablePool**: Manages the collection of reusable objects (often implemented as Singleton) + +## Example + +Think of an office warehouse management system. When a new employee joins, the manager checks the warehouse for spare equipment (monitors, keyboards, chairs). If available, the equipment is given to the employee. If not available, new equipment is purchased. When an employee leaves, their equipment is returned to the warehouse for future use. + +Database connection pools are a classic real-world example. Creating database connections is expensive, so applications maintain a pool of connections that can be shared among different operations, significantly improving performance. + +## Checklist + +1. Identify expensive-to-create objects that are frequently used +2. Create a pool class (usually a Singleton) to manage object instances +3. Implement `acquire()` method to get objects from the pool +4. Implement `release()` method to return objects to the pool +5. Handle pool empty scenarios (create new objects or wait) +6. Optionally implement pool size limits and cleanup mechanisms +7. Ensure thread safety if the pool will be accessed concurrently +8. Consider object reset/cleanup when returning objects to the pool + +## Pros and Cons + +**Pros:** +- Significantly improves performance for expensive object creation +- Reduces memory allocation and garbage collection overhead +- Controls resource usage by limiting object instances +- Provides better resource management and monitoring +- Reduces system load and improves scalability + +**Cons:** +- Adds complexity to object lifecycle management +- Risk of memory leaks if objects are not properly returned to the pool +- Thread safety considerations in multi-threaded environments +- Pool management overhead for simple objects might outweigh benefits +- Potential for stale or corrupted objects if not properly reset + +## Rules of Thumb + +- Use when object creation is expensive relative to object usage +- Most beneficial when object creation cost is high and object usage patterns are predictable +- Consider thread safety requirements - pools are often shared resources +- Implement proper object reset mechanisms to avoid state pollution +- Monitor pool usage to tune pool size for optimal performance +- Don't use for lightweight objects where creation cost is minimal +- Often combined with Factory pattern for object creation within the pool +- Consider using with Singleton pattern for the pool manager itself diff --git a/src/content/design-pattern/posts/observer.mdx b/src/content/design-pattern/posts/observer.mdx new file mode 100644 index 0000000..9e844e3 --- /dev/null +++ b/src/content/design-pattern/posts/observer.mdx @@ -0,0 +1,69 @@ +--- +layout: post +title: Observer Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/observer-pattern-set-1-introduction +refactoring: observer +sourcemaking: observer +wikipedia: Observer_pattern +scaler: design-patterns/observer-design-pattern/ +--- + +## Intent +- Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. +- Encapsulate the core (or common or engine) components in a Subject abstraction, and the variable (or optional or user interface) components in an Observer hierarchy. +- The "View" part of Model-View-Controller. + +## Problem + +You have a subscription service where customers want to be notified about different events. For example, in an e-commerce application, customers might want to be notified when: +- A product comes back in stock +- A price drops below a certain threshold +- A new product in their favorite category is added + +The naive approach would be to have the store check all customers and their preferences periodically, but this leads to wasted resources. Alternatively, customers could constantly check the store, but this creates unnecessary network traffic and poor user experience. + +## Structure + +The Observer pattern consists of two main players: the Subject (Publisher) and the Observer (Subscriber). The subject maintains a list of observers and notifies them of any state changes, usually by calling one of their methods. + + + +The key is that the subject and observers are loosely coupled. The subject knows only that observers implement a certain interface, but it doesn't need to know their concrete classes, what they do, or why they're interested. + +## Example + +Think of a magazine subscription service. You (the subscriber) express interest in a particular magazine and provide your address. The magazine publisher maintains a list of subscribers and automatically sends new issues to everyone on the list. You can unsubscribe at any time, and the publisher will stop sending magazines to your address. + +Similarly, in software, a weather station (subject) can notify multiple display devices (observers) when weather data changes, without knowing the specific details of each display implementation. + +## Checklist + +1. Differentiate between the core (independent) functionality and the optional (dependent) functionality. +2. Model the independent functionality with a "subject" abstraction. +3. Model the dependent functionality with an "observer" hierarchy. +4. The Subject is coupled only to the Observer base class. +5. The client configures the number and type of Observers. +6. Observers register themselves with the Subject. +7. The Subject broadcasts events to all registered Observers. +8. The Subject may "push" information at the Observers, or, the Observers may "pull" the information they need from the Subject. + +## Pros and Cons + +**Pros:** +- **Open/Closed Principle** - You can introduce new subscriber classes without having to change the publisher's code. +- **Runtime relationships** - You can establish relations between objects at runtime. +- **Loose coupling** - Publishers and subscribers are independent of each other. + +**Cons:** +- **Random notification order** - Subscribers get notified in random order. +- **Memory leaks** - If observers are not properly removed, memory leaks can occur. + +## Rules of Thumb + +- Chain of Responsibility, Command, Mediator, and Observer, address how you can decouple senders and receivers, but with different trade-offs. Observer defines a one-to-many relationship between objects. +- Mediator and Observer are competing patterns. The difference between them is that Observer distributes communication by introducing "observer" and "subject" objects, whereas a Mediator object encapsulates the communication between other objects. +- Observer can use Mediator to notify observers instead of direct communication. diff --git a/src/content/design-pattern/posts/private-class-data.mdx b/src/content/design-pattern/posts/private-class-data.mdx new file mode 100644 index 0000000..9fb13a3 --- /dev/null +++ b/src/content/design-pattern/posts/private-class-data.mdx @@ -0,0 +1,74 @@ +--- +layout: post +title: Private Class Data Design Pattern +difficulty: medium +tags: [structural] +langs: [java, py, cpp] +companies: [] +refactoring: private-class-data +sourcemaking: private_class_data +wikipedia: Private_class_data_pattern +scaler: design-patterns/private-class-data-design-pattern/ +--- + +## Intent +- Control write access to class attributes after object construction +- Separate data from methods that use it for better organization +- Encapsulate class data initialization and provide immutability after construction + +## Problem + +A class may expose its attributes to manipulation when such manipulation is no longer desirable, especially after the object has been fully constructed. Traditional access modifiers don't provide fine-grained control over when attributes can be modified. + +Consider a `Person` class with attributes like name, birthDate, and socialSecurityNumber. Once a person object is created, these attributes should be immutable, but the programming language might not provide a way to make them truly final after construction while still allowing initialization flexibility. + +## Structure + +The Private Class Data pattern addresses this by extracting data into a separate class that controls access to the attributes. + +The pattern involves: +- **Main Class**: The primary class that needs to protect its data +- **Data Class**: A separate class that holds the sensitive attributes +- **Controlled Access**: The data class provides controlled access through getters and limited setters + +## Example + +Think of a bank account system. Once an account is created with an account number, customer details, and initial balance, certain information like the account number and customer ID should never change. The Private Class Data pattern allows you to separate this immutable data into a protected data class while still allowing operations like balance updates through controlled methods. + +Another example is a configuration class where settings should be read-only after initialization but need to be set during the object creation phase. + +## Checklist + +1. Identify attributes that need protection from modification +2. Create a dedicated data class to hold these attributes +3. Move the protected attributes to the data class +4. Create an instance of the data class in the main class +5. Initialize the data class through its constructor +6. Expose each attribute through getter methods in the data class +7. Only expose setters for attributes that truly need post-construction modification +8. Ensure the main class accesses data only through the data class interface + +## Pros and Cons + +**Pros:** +- Provides fine-grained control over attribute access +- Improves data integrity by preventing unintended modifications +- Separates data concerns from business logic +- Enables creation of immutable-like behavior even in languages without strong immutability support +- Makes the design more explicit about what data can and cannot be changed + +**Cons:** +- Increases complexity by adding an extra layer of indirection +- Can lead to more verbose code with additional getter/setter methods +- May impact performance due to extra method calls +- Can make the code harder to understand for developers unfamiliar with the pattern +- Might be overkill for simple classes with minimal data protection needs + +## Rules of Thumb + +- Use when you need "final after construction" behavior that your language doesn't directly support +- Consider this pattern for classes with sensitive data that should be immutable after initialization +- Don't use if simple access modifiers (private, protected) are sufficient for your needs +- The data class can often be implemented as an inner class to maintain close coupling +- Consider combining with Factory pattern for complex initialization scenarios +- If you need true immutability, consider using immutable data structures or value objects instead diff --git a/src/content/design-pattern/posts/prototype.mdx b/src/content/design-pattern/posts/prototype.mdx new file mode 100644 index 0000000..d32104c --- /dev/null +++ b/src/content/design-pattern/posts/prototype.mdx @@ -0,0 +1,68 @@ +--- +layout: post +title: Prototype Design Pattern +difficulty: medium +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: system-design/prototype-design-pattern +refactoring: prototype +sourcemaking: prototype +wikipedia: Prototype_pattern +scaler: design-patterns/prototype-design-pattern/ +--- + +## Intent +- Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype +- Co-opt one instance of a class for use as a breeder of all future instances +- The new operator considered harmful when the exact class of object that must be created changes frequently + +## Problem + +Say you have an object, and you want to create an exact copy of it. How would you do it? First, you have to create a new object of the same class. Then you have to go through all the fields of the original object and copy their values over to the new object. + +Nice! But there's a catch. Not all objects can be copied that way because some of the object's fields may be private and not visible from outside the object itself. There's one more problem with the direct approach. Since you have to know the object's class to create a duplicate, your code becomes dependent on that class. + +## Structure + +The Prototype pattern delegates the cloning process to the actual objects that are being cloned. The pattern declares a common interface for all objects that support cloning. This interface lets you clone an object without coupling your code to the class of that object. + + + +Usually, such an interface contains just a single `clone` method. The implementation of the `clone` method is very similar in all classes. The method creates an object of the current class and carries over all of the field values of the old object into the new one. + + + +## Example + +In biology, mitotic cell division results in a pair of identical cells. The original cell acts as a prototype and takes an active role in creating the copy. Similarly, the Prototype pattern lets objects clone themselves. + +Think of the prototype as a "template" object that contains the standard configurations. When you need a new object, you clone the prototype and then modify the clone according to your specific requirements, rather than going through the expensive process of initializing a new object from scratch. + +## Implementation Checklist + +1. Create the prototype interface and declare the `clone` method in it. Or just add the method to all classes of an existing class hierarchy +2. A prototype class must define the alternative constructor that accepts an object of that class as an argument. The constructor must copy the values of all fields defined in the class from the passed object into the newly created instance +3. The cloning method usually consists of just one line: running a `new` operator with the prototypical version of the constructor. Note, that every class must explicitly override the cloning method and use its own class name along with the `new` operator +4. Optionally, create a centralized prototype registry to store a catalog of frequently used prototypes + +## Pros and Cons + +**Pros:** +- Clone objects without coupling to their concrete classes +- Get rid of repeated initialization code in favor of cloning pre-built prototypes +- Produce complex objects more conveniently +- Get an alternative to inheritance when dealing with configuration presets for complex objects + +**Cons:** +- Cloning complex objects that have circular references might be very tricky + +## Rules of Thumb + +- Many designs start out using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated) +- Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes +- Prototype can help when you need to save copies of Commands in history +- Designs that make heavy use of Composite and Decorator can often benefit from Prototype. Applying the pattern lets you clone complex structures instead of re-constructing them from scratch +- Prototype isn't based on inheritance, so it doesn't have inheritance's drawbacks. On the other hand, Prototype requires a complicated initialization of the cloned object. Factory Method is based on inheritance but doesn't require an initialization step +- Sometimes Prototype can be a simpler alternative to Memento. This works if the object, the state of which you want to store in the history, is fairly straightforward and doesn't have links to external resources, or the links are easy to re-establish +- Abstract Factories, Builders and Prototypes can all be implemented as Singletons diff --git a/src/content/design-pattern/posts/proxy.mdx b/src/content/design-pattern/posts/proxy.mdx new file mode 100644 index 0000000..a296c3e --- /dev/null +++ b/src/content/design-pattern/posts/proxy.mdx @@ -0,0 +1,81 @@ +--- +layout: post +title: Proxy Design Pattern +difficulty: medium +tags: [structural] +langs: [java, py, cpp] +companies: [] +gfg: system-design/proxy-design-pattern +refactoring: proxy +sourcemaking: proxy +wikipedia: Proxy_pattern +scaler: design-patterns/proxy-design-pattern/ +--- + +## Intent +- Provide a surrogate or placeholder for another object to control access to it. +- Use an extra level of indirection to support distributed, controlled, or intelligent access. +- Add a wrapper and delegation to protect the real component from undue complexity. + +## Problem +Why would you want to control access to an object? Here is an example: you have a massive object that consumes a vast amount of system resources. You need it from time to time, but not always. + +You could implement lazy initialization: create this object only when it's actually needed. All of the object's clients would need to execute some deferred initialization code. Unfortunately, this would probably cause a lot of code duplication. + +In an ideal world, we'd want to put this code directly into our object's class, but that isn't always possible. For instance, the class may be part of a closed 3rd-party library. + +## Structure + +The Proxy pattern suggests that you create a new proxy class with the same interface as an original service object. Then you update your app so that it passes the proxy object to all of the original object's clients. Upon receiving a request from a client, the proxy creates a real service object and delegates all the work to it. + + + +The proxy disguises itself as a database object. It can handle lazy initialization and result caching without the client or the real database object even knowing. + + + +## Example + +A credit card is a proxy for a bank account, which is a proxy for a bundle of cash. Both implement the same interface: they can be used for making a payment. A consumer feels great because there's no need to carry loads of cash around. A shop owner is also happy since the income from a transaction gets added electronically to the shop's bank account without the risk of depositing or losing the cash. + + + +The proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object. + +## Checklist + +1. If there's no pre-existing service interface, create one to make proxy and service objects interchangeable. Extracting the interface from the service class isn't always possible, because you'd need to change all of the service's clients to use that interface. Plan B is to make the proxy a subclass of the service class, and this way it'll inherit the interface of the service. +2. Create the proxy class. It should have a field for storing a reference to the service. Usually, proxies create and manage the whole lifecycle of their services. On rare occasions, a service is passed to the proxy via a constructor by the client. +3. Implement the proxy methods according to their purposes. In most cases, after doing some work, the proxy should delegate the work to the service object. +4. Consider introducing a creation method that decides whether the client gets a proxy or a real service. This can be a simple static method in the proxy class or a full-blown factory method. +5. Consider implementing lazy initialization for the service object. + +## Proxy Types + +**Virtual Proxy** - Controls access to a resource that is expensive to create. It creates the resource on demand. + +**Remote Proxy** - Controls access to a resource that is located remotely (different address space, network, etc.). + +**Protection Proxy** - Controls access to a resource based on access rights. It checks whether the caller has necessary permissions. + +**Smart Reference** - Performs additional actions when an object is accessed (reference counting, loading from database, locking, etc.). + +## Pros and Cons + +**Pros:** +- You can control the service object without clients knowing about it +- You can manage the lifecycle of the service object when clients don't care about it +- The proxy works even if the service object isn't ready or is not available +- Open/Closed Principle: You can introduce new proxies without changing the service or clients + +**Cons:** +- The code may become more complicated since you need to introduce a lot of new classes +- The response from the service might get delayed +- Additional layer of indirection may impact performance + +## Rule of thumb + +- Adapter provides a different interface to the wrapped object, Proxy provides the same interface, and Decorator provides an enhanced interface. +- Decorator and Proxy have similar structures, but very different intents. Both patterns are built on the composition principle, where one object is supposed to delegate some of the work to another. The difference is that a Proxy usually manages the lifecycle of its service object on its own, whereas the composition of Decorators is always controlled by the client. +- Facade is similar to Proxy in that both buffer a complex entity and initialize it on their own. Unlike Facade, Proxy has the same interface as its service object, which makes them interchangeable. +- Proxy and Decorator implement the same interface, but Decorator adds behavior while Proxy controls access. diff --git a/src/content/design-pattern/posts/singleton.mdx b/src/content/design-pattern/posts/singleton.mdx new file mode 100644 index 0000000..91aa3d7 --- /dev/null +++ b/src/content/design-pattern/posts/singleton.mdx @@ -0,0 +1,73 @@ +--- +layout: post +title: Singleton Design Pattern +difficulty: easy +tags: [creational] +langs: [java, py, cpp] +companies: [] +gfg: design-patterns/singleton-design-pattern +refactoring: singleton +sourcemaking: singleton +wikipedia: Singleton_pattern +scaler: design-patterns/singleton-design-pattern/ +--- + +## Intent +- Ensure a class only has one instance, and provide a global point of access to it +- Encapsulated "just-in-time initialization" or "initialization on first use" +- Control instantiation of a class to one and only one instance + +## Problem + +The Singleton pattern solves two problems at the same time, violating the Single Responsibility Principle: + +**Ensure that a class has just a single instance.** Why would anyone want to control how many instances a class has? The most common reason is to control access to some shared resource—for example, a database or a file. Here's how it works: imagine that you created an object, but after a while decided to create a new one. Instead of receiving a fresh object, you'll get the one you already created. + +**Provide a global access point to that instance.** Remember those global variables that you (all right, me) used to store some essential objects? While they're very handy, they're also very unsafe since any code can potentially overwrite the contents of those variables and crash the app. + +## Structure + +The Singleton class declares the static method `getInstance` that returns the same instance of its own class. The Singleton's constructor should be hidden from the client code. Calling the `getInstance` method should be the only way of getting the Singleton object. + + + +All implementations of the Singleton have these two steps in common: +- Make the default constructor private, to prevent other objects from using the `new` operator with the Singleton class +- Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and saves it in a static field + + + +## Example + +The government is an excellent example of the Singleton pattern. A country can have only one official government. Regardless of the personal identities of the individuals who form governments, the title, "The Government of X", is a global point of access that identifies the group of people in charge. + +Similarly, a database connection pool, cache, thread pool, or logger are real-world examples where you want to ensure only one instance exists to avoid conflicts and maintain consistency. + +## Implementation Checklist + +1. Add a private static field to the class for storing the singleton instance +2. Declare a public static creation method for getting the singleton instance +3. Implement "lazy initialization" inside the static method. It should create a new object on its first call and put it into the static field. The method should always return that instance on all subsequent calls +4. Make the constructor of the class private. The static method of the class will still be able to call the constructor, but not the other objects +5. Go over the client code and replace all direct calls to the singleton's constructor with calls to its static creation method + +## Pros and Cons + +**Pros:** +- Guarantees that a class has only a single instance +- Gain a global access point to that instance +- The singleton object is initialized only when it's requested for the first time + +**Cons:** +- Violates the Single Responsibility Principle. The pattern solves two problems at the time +- The Singleton pattern can mask bad design, for instance, when the components of the program know too much about each other +- The pattern requires special treatment in a multithreaded environment so that multiple threads won't create a singleton object several times +- It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects + +## Rules of Thumb + +- A Facade class can often be transformed into a Singleton since a single facade object is sufficient in most cases +- Flyweight would resemble Singleton if you somehow managed to reduce all shared states of the objects to just one flyweight object. But there are two fundamental differences between these patterns: there should be only one Singleton instance, whereas a Flyweight class can have multiple instances with different intrinsic states +- Abstract Factories, Builders and Prototypes can all be implemented as Singletons +- State objects are often singletons +- Singleton should be considered only if all three of the following criteria are satisfied: ownership of the single instance cannot be reasonably assigned, lazy initialization is desirable, and global access is not otherwise provided diff --git a/src/content/design-pattern/posts/state.mdx b/src/content/design-pattern/posts/state.mdx new file mode 100644 index 0000000..6cad043 --- /dev/null +++ b/src/content/design-pattern/posts/state.mdx @@ -0,0 +1,75 @@ +--- +layout: post +title: State Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/state-design-pattern +refactoring: state +sourcemaking: state +wikipedia: State_pattern +scaler: design-patterns/state-design-pattern/ +--- + +## Intent +- Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. +- An object-oriented state machine. +- Wrapper + polymorphism + collaboration. + +## Problem + +When an object's behavior changes drastically based on its internal state, implementing this with conditional statements leads to complex, hard-to-maintain code. Consider a media player application where the behavior of play, pause, and stop buttons changes based on whether the player is in a "playing", "stopped", or "locked" state. + +Using traditional if/else or switch statements throughout the class creates several problems: +- The class becomes bloated with numerous conditional statements +- Adding new states requires modifying existing conditional logic +- The code becomes fragile and error-prone +- State-specific behavior is scattered across multiple methods + +## Structure + +The State pattern extracts state-related behaviors into separate state classes and makes the original object delegate work to an instance of one of these classes, instead of acting on its own. + + + +The pattern consists of: +- **Context** - maintains a reference to one of the concrete state objects and delegates state-specific work to it +- **State interface** - declares methods that all concrete states should implement +- **Concrete States** - provide their own implementations for state-specific methods + +## Example + +Consider a document editor where documents can be in different states: Draft, Moderation, and Published. The available actions and their behaviors change based on the current state: + +- **Draft state**: User can modify content, submit for review +- **Moderation state**: Admin can approve/reject, user can only view +- **Published state**: Content is read-only for users, admin can unpublish + +Instead of having complex conditionals in every method, each state is a separate class that knows how to handle specific actions and when to transition to other states. + +## Checklist + +1. Identify an existing class or create a new class that will serve as the "context". +2. Define the State base class that replicates all the methods of the context class. Don't worry about implementing these methods yet. +3. Create classes for each state of the context. These classes should extend the State base class and override the methods that are relevant for that state. +4. In the context class, add a reference field for storing the current state object and a method for changing it. +5. Replace all conditional logic in the context with calls to methods of the state object. +6. To switch the state, simply assign a new state object to the context. + +## Pros and Cons + +**Pros:** +- **Single Responsibility Principle** - Organize code related to particular states into separate classes. +- **Open/Closed Principle** - Introduce new states without changing existing state classes or context. +- **Simplified context** - Eliminate bulky conditional statements from context class. + +**Cons:** +- **Overkill** - May be overkill if state machine has few states or rarely changes. +- **Complexity** - Can introduce unnecessary complexity for simple state machines. + +## Rules of Thumb + +- State can be regarded as an extension of Strategy. Both patterns are based on composition: they change the behavior of the context by delegating work to helper objects. Strategy makes these objects completely independent and unaware of each other. However, State doesn't restrict dependencies between concrete states, letting them alter the state of the context at will. +- State, Strategy, Bridge (and to some degree Adapter) have very similar solution structures. They all share the same solution: "composition". However, they all solve different problems. +- State pattern is closely related to the concept of Finite-State Machine. The main idea is that at any given moment, there's a finite number of states which a program can be in. diff --git a/src/content/design-pattern/posts/strategy.mdx b/src/content/design-pattern/posts/strategy.mdx new file mode 100644 index 0000000..09bf6ff --- /dev/null +++ b/src/content/design-pattern/posts/strategy.mdx @@ -0,0 +1,80 @@ +--- +layout: post +title: Strategy Design Pattern +difficulty: easy +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/strategy-pattern-set-1 +refactoring: strategy +sourcemaking: strategy +wikipedia: Strategy_pattern +scaler: design-patterns/strategy-design-pattern/ +--- + +## Intent +- Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. +- Capture the abstraction in an interface, bury implementation details in derived classes. + +## Problem + +Imagine you're developing a navigation app that can calculate routes for different transportation modes: walking, driving, cycling, and public transport. Initially, you might implement all routing algorithms directly in the main navigation class. + +However, this approach leads to several problems: +- The navigation class becomes bloated with multiple algorithms +- Adding new routing methods requires modifying the existing class +- Testing becomes difficult as all algorithms are tightly coupled +- Team collaboration becomes challenging due to frequent conflicts in the same large class + +The class essentially violates the Single Responsibility Principle by handling multiple algorithms. + +## Structure + +The Strategy pattern suggests extracting varying algorithms into separate classes called strategies, which all implement a common interface. The original class (context) stores a reference to one of the strategies and delegates the work to it. + + + +Key participants: +- **Strategy interface** - common to all concrete strategies, declares methods the context uses to execute a strategy +- **Concrete strategies** - implement different variations of an algorithm +- **Context** - maintains a reference to a strategy object and communicates with strategies only via the strategy interface + +## Example + +Consider a payment processing system in an e-commerce application. Different payment methods (credit card, PayPal, cryptocurrency) require different processing algorithms: + +- **Credit card strategy**: Validates card number, processes through banking API +- **PayPal strategy**: Redirects to PayPal, handles OAuth authentication +- **Cryptocurrency strategy**: Validates wallet address, processes blockchain transaction + +Without Strategy pattern, the payment processor would contain complex conditionals for each payment type. With Strategy pattern, each payment method becomes a separate strategy class implementing a common `PaymentStrategy` interface. + +## Checklist + +1. Identify an algorithm that's prone to frequent changes or has multiple variants. +2. Declare the strategy interface common to all variants of the algorithm. +3. Extract all algorithms into their own classes. They should all implement the strategy interface. +4. In the context class, add a field for storing a reference to a strategy object and provide a setter for changing it. +5. Replace calls to the algorithm with calls to the strategy object's method. +6. The client should associate the context with a suitable strategy. + +## Pros and Cons + +**Pros:** +- **Runtime algorithm switching** - You can swap algorithms used inside an object at runtime. +- **Code isolation** - You can isolate the implementation details of an algorithm from the code that uses it. +- **Composition over inheritance** - You can replace inheritance with composition. +- **Open/Closed Principle** - You can introduce new strategies without changing the context. + +**Cons:** +- **Increased complexity** - If you have only a few algorithms and they rarely change, there's no real reason to overcomplicate the program. +- **Client awareness** - Clients must be aware of the differences between strategies to be able to select a proper one. +- **Modern languages** - A lot of modern programming languages have functional type support that lets you implement different versions of an algorithm inside a set of anonymous functions. + +## Rules of Thumb + +- Strategy is like Template Method except in its granularity. +- State is like Strategy except in its intent. +- Strategy lets you change the guts of an object. Decorator lets you change the skin. +- State, Strategy, Bridge (and to some degree Adapter) have very similar structures. They're all based on composition, which is delegating work to other objects. +- Strategy has 2 different implementations, the first is similar to State. The difference is in binding times (Strategy is a bind-once pattern, whereas State is more dynamic). diff --git a/src/content/design-pattern/posts/template-method.mdx b/src/content/design-pattern/posts/template-method.mdx new file mode 100644 index 0000000..4378913 --- /dev/null +++ b/src/content/design-pattern/posts/template-method.mdx @@ -0,0 +1,83 @@ +--- +layout: post +title: Template Method Design Pattern +difficulty: medium +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/template-method-design-pattern +refactoring: template-method +sourcemaking: template-method +wikipedia: Template_method_pattern +scaler: design-patterns/template-method-design-pattern/ +--- + +## Intent +- Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. +- Base class declares algorithm 'placeholders', and derived classes implement the placeholders. + +## Problem + +Imagine you're developing a data analysis application that processes different file formats (CSV, JSON, XML). Each format requires similar processing steps: open file, parse data, analyze data, generate report, and close file. However, the specific implementation of parsing varies for each format. + +Without Template Method, you might end up with: +- Code duplication across different format processors +- Inconsistent processing workflows +- Difficulty maintaining the overall algorithm structure +- Risk of missing steps in new format implementations + +Each processor class would need to implement the entire workflow, leading to scattered and duplicate code for common operations like opening files and generating reports. + +## Structure + +The Template Method pattern defines the skeleton of an algorithm in a base class and lets subclasses override specific steps without changing the overall structure. + + + +Key components: +- **Abstract Class** - declares methods that act as steps of an algorithm, plus the actual template method +- **Concrete Classes** - can override all steps, but not the template method itself + +## Example + +Consider a social media posting application that supports different platforms (Facebook, Twitter, LinkedIn). The posting process follows these steps: + +1. Authenticate with the platform +2. Prepare content (format according to platform rules) +3. Post content +4. Handle response +5. Log activity + +While the overall workflow remains the same, each platform has specific requirements: +- **Facebook**: Supports rich text, images, longer posts +- **Twitter**: Character limit, hashtag optimization, threading +- **LinkedIn**: Professional tone, article format, networking features + +The Template Method pattern allows you to define the posting workflow once while letting each platform implement its specific formatting and posting logic. + +## Checklist + +1. Identify the parts of an algorithm that are invariant and the parts that are variant. +2. Create an abstract base class and declare the template method and the set of abstract "primitive" operations. +3. Implement the invariant parts of the algorithm in the template method. +4. Identify the parts that require variants and make them abstract methods. +5. For each variant, create a concrete derived class that implements all abstract methods. + +## Pros and Cons + +**Pros:** +- **Code reuse** - You can pull the duplicate code into a superclass. +- **Control extension** - You can let clients extend only particular steps of an algorithm, but not the whole algorithm or its structure. +- **Framework creation** - Perfect for creating frameworks where the framework defines the workflow and users provide specific implementations. + +**Cons:** +- **Limited flexibility** - You're restricted by the provided skeleton of an algorithm. +- **Liskov Substitution Principle** - Some clients may be limited by the provided skeleton of an algorithm. +- **Maintenance difficulty** - Template methods tend to be harder to maintain the more steps they have. + +## Rules of Thumb + +- Strategy is like Template Method except in its granularity. Template Method uses inheritance to vary part of an algorithm. Strategy uses delegation to vary the entire algorithm. +- Template Method is based on inheritance: it lets you alter parts of an algorithm by extending those parts in subclasses. Strategy is based on composition: you can alter parts of the object's behavior by supplying it with different strategies that correspond to that behavior. +- Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method. +- Template Methods are frequently used in frameworks. Developers can extend framework classes and override template method steps to customize processing logic. diff --git a/src/content/design-pattern/posts/visitor.mdx b/src/content/design-pattern/posts/visitor.mdx new file mode 100644 index 0000000..8bed02b --- /dev/null +++ b/src/content/design-pattern/posts/visitor.mdx @@ -0,0 +1,67 @@ +--- +layout: post +title: Visitor Design Pattern +difficulty: hard +tags: [behavioral] +langs: [java, py, cpp] +companies: [] +gfg: system-design/visitor-design-pattern +refactoring: visitor +sourcemaking: visitor +wikipedia: Visitor_pattern +scaler: design-patterns/visitor-design-pattern/ +--- + +## Intent + +- Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. +- Define a family of algorithms, encapsulate each one, and make them interchangeable. Visitor lets the algorithm vary independently from the clients that use it. +- The classic technique for recovering lost type information. + +## Problem + +You have a complex object structure (like a tree or graph) with different types of elements, and you want to perform various operations on these elements. Adding new operations would require modifying all the element classes, which violates the Open/Closed Principle and can lead to code bloat. + +Consider a file system with different types of nodes - files and directories. If you want to add operations like calculating total size, virus scanning, or generating reports, you'd have to modify each node class every time you add a new operation. + +## Solution + +The Visitor pattern suggests placing the new behavior into a separate class called visitor, instead of trying to integrate it into existing classes. The visitor is passed to elements as an argument of a special "accept" method, which dispatches the call to the proper visitor's method corresponding to the current element's class. + + + +This technique allows you to add new operations without modifying existing element classes. It uses a technique called double dispatch - the operation executed depends on both the type of Visitor and the type of Element. + +## Structure + + + +The pattern consists of: +1. **Visitor Interface** - declares visit methods for each concrete element class +2. **Concrete Visitors** - implement visitor interface and define operations for each element type +3. **Element Interface** - declares accept method that takes a visitor +4. **Concrete Elements** - implement accept method and call appropriate visitor method + +## Example + +The Visitor pattern is like a taxi company dispatch system. When you call for a taxi (visitor), the dispatcher (client) sends a taxi to your location (element). The taxi driver (visitor's operation) then performs the service based on your specific needs and location type. + + + +## Checklist + +1. Confirm that the existing hierarchy will be fairly stable and that the public interface of these classes is sufficient for the visitor to accomplish its work. +2. Create a Visitor base class with a "visit" method signatures for each derived type in the hierarchy. +3. Add a single pure virtual "accept" method to the base class of the Element hierarchy. accept(Visitor) functionality is "dispatch off this polymorphic element to the polymorphic visitor". +4. In each derived Element class, implement the accept method as simply: visitor.visit(this). +5. The Element classes are now closed (no more modifications). Create a Visitor derived class for each operation to be performed on Element objects. +6. The client creates Visitor objects and passes each to Element objects by calling accept. + +## Rule of thumb + +- Visitor's primary purpose is to abstract functionality that can be applied to an aggregate hierarchy of "element" objects. +- The approach encourages designing lightweight Element classes - because processing functionality is removed from their list of responsibilities. +- New functionality can easily be added to the original inheritance hierarchy by creating a new Visitor subclass. +- Visitor implements "double dispatch". OO messages routinely manifest "single dispatch" - the operation that is executed depends on: the name of the request, and the type of the receiver. In "double dispatch", the operation executed depends on: the name of the request, and the type of TWO receivers (the type of the Visitor and the type of the element it visits). +- The Visitor pattern should be used when you have a stable class hierarchy that you want to define new operations over without altering the classes. +- Visitor, like the other design patterns, should be used judiciously. The pattern's abuses and over-use will result in code that is hard to understand and maintain.