⚙️ C++

أنماط التصميم في لغة C++: دليل مفصل

What is it and why? (a real-life analogy)

Imagine you are building a house. You have two paths:

  • Path one (like a beginner): You start from scratch — you mix the concrete yourself, you saw the boards yourself, you figure out how to connect the walls yourself. You build each new house from scratch, reinventing the wheel every time. If you made a crooked roof on the first house, you will repeat the same mistake on the second.
  • Path two (like a professional): You use proven blueprints. For the foundation — a standard "Strip Foundation" design. For the roof — a "Gable Roof with Support Beams". You don't invent them from scratch, you simply apply ready-made solutions that have already proven their reliability.

Design Patterns are exactly these kinds of "standard blueprints" for a programmer. This is not ready-made code, but proven solution schemes for frequently occurring problems. They weren't born yesterday — the world's best programmers have been collecting and describing them for decades.

Important: A pattern is not a library that you can download. It is an idea that you implement in code yourself. A pattern answers the question "how to organize code so that it is flexible and doesn't fall apart when changes are made."

Why does a school student or a beginner need them? Imagine you are writing a game. You have a hero, monsters, and weapons. If you just start writing code "as it comes," then in a week you will get confused: where is the monster created? How does the hero receive damage? Why does everything break when adding a new type of weapon? Patterns give you a ready-made "architecture" — you know that monsters are created by a Factory, and the hero receives damage through an Observer. The code becomes as clear as Lego instructions.

Theory: how it works from the inside

In the programming world, there are 23 classic patterns described in the "Gang of Four" book. They are divided into three groups:

Group What it does Examples
Creational Decide how to create objects. Hide the complexity of creation. Singleton, Factory, Builder
Structural Decide how to assemble objects into larger structures. Adapter, Decorator, Composite
Behavioral Decide how objects communicate with each other. Strategy, Observer, Command

How does it work from the inside? A pattern is always about connections between classes. Instead of rigidly tying one class to another (for example, the Player class directly creates the Sword class), we introduce an intermediate layer. This intermediate layer (interface, abstract class) allows you to replace parts of the system without breaking the rest.

For example, the Strategy pattern says: "Extract the changing behavior into a separate class and pass it as a parameter." Internally, it looks like this: there is an interface AttackMethod, it has implementations SwordAttack and BowAttack. The player simply stores a reference to AttackMethod and calls the attack() method. Want to change the weapon? Just plug in a different strategy.

Syntax in C++: all forms and variants

In C++, patterns are implemented through standard language mechanisms: classes, inheritance, virtual functions, templates, and smart pointers. Let's look at the key elements that will be needed for any pattern.

1. Abstract class (interface)

This is the "skeleton" of the future object. C++ does not have the interface keyword like Java, so the role of an interface is played by a class where all methods are pure virtual (= 0).

// Abstract class — you cannot create its object, you can only inherit from it
class Weapon {
public:
    // Pure virtual function — the descendant must implement it
    virtual void attack() = 0;
    // Virtual destructor — mandatory for correct deletion via a pointer to the base class
    virtual ~Weapon() = default;
};

2. Inheritance and overriding

Descendant classes must implement all pure virtual methods.

class Sword : public Weapon {
public:
    void attack() override {  // override — checks that we are actually overriding the method
        std::cout << "Swing the sword!" << std::endl;
    }
};

class Bow : public Weapon {
public:
    void attack() override {
        std::cout << "Shoot the bow!" << std::endl;
    }
};

3. Polymorphism through pointers

The main power of patterns — we work with the base type, and the actual object is determined at runtime.

Weapon* myWeapon = new Sword();
myWeapon->attack();  // Outputs: "Swing the sword!"

// Change behavior on the fly
myWeapon = new Bow();
myWeapon->attack();  // Outputs: "Shoot the bow!"

delete myWeapon;  // Don't forget to delete!

4. Smart Pointers

In modern C++ (C++11 and later), we avoid manual new/delete. We use std::unique_ptr and std::shared_ptr. This manages memory automatically.

#include <memory>

std::unique_ptr<Weapon> createWeapon(const std::string& type) {
    if (type == "sword") {
        return std::make_unique<Sword>();
    } else if (type == "bow") {
        return std::make_unique<Bow>();
    }
    return nullptr;
}

// Usage
auto wp = createWeapon("sword");
wp->attack();  // Memory is freed automatically