Problem :

Let’s say we want to communicate from system A to system B. ( Device and Remote ), Device can be anything (TV, fan, sensor), similarly for remote.

#include <iostream>
using namespace std;

// Each device has its own remote implementation

// ---- TV Remotes ----
class BasicTVRemote {
public:
    void pressButton() {
        cout << "Basic Remote: TV turned on\n";
    }
};

class AdvancedTVRemote {
public:
    void pressButton() {
        cout << "Advanced Remote: TV voice command activated\n";
    }
};

// ---- Fan Remotes ----
class BasicFanRemote {
public:
    void pressButton() {
        cout << "Basic Remote: Fan turned on\n";
    }
};

class AdvancedFanRemote {
public:
    void pressButton() {
        cout << "Advanced Remote: Fan turned on with timer\n";
    }
};

// ---- Main function ----
int main() {
    BasicTVRemote basicTV;
    AdvancedTVRemote advTV;
    BasicFanRemote basicFan;
    AdvancedFanRemote advFan;

    basicTV.pressButton();
    advTV.pressButton();
    basicFan.pressButton();
    advFan.pressButton();

    return 0;
}

#include <iostream>
#include <memory>
using namespace std;

// ---- Implementor Interface ----
class Device {
public:
    virtual void turnOn() = 0;
    virtual ~Device() = default;
};

// ---- Concrete Devices ----
class TV : public Device {
public:
    void turnOn() override {
        cout << "TV turned on\n";
    }
};

class Fan : public Device {
public:
    void turnOn() override {
        cout << "Fan turned on\n";
    }
};

// ---- Abstraction ----
class RemoteControl {
protected:
    Device* device;  // Bridge
public:
    RemoteControl(Device* d) : device(d) {}
    virtual void pressButton() = 0;
    virtual ~RemoteControl() = default;
};

// ---- Refined Abstractions ----
class BasicRemote : public RemoteControl {
public:
    BasicRemote(Device* d) : RemoteControl(d) {}
    void pressButton() override {
        cout << "Basic Remote: ";
        device->turnOn();
    }
};

class AdvancedRemote : public RemoteControl {
public:
    AdvancedRemote(Device* d) : RemoteControl(d) {}
    void pressButton() override {
        cout << "Advanced Remote with voice: ";
        device->turnOn();
    }
};

// ---- Main function ----
int main() {
    TV tv;
    Fan fan;

    BasicRemote basicTV(&tv);
    AdvancedRemote advTV(&tv);

    BasicRemote basicFan(&fan);
    AdvancedRemote advFan(&fan);

    basicTV.pressButton();
    advTV.pressButton();
    basicFan.pressButton();
    advFan.pressButton();

    return 0;
}

You already have:

  • 2 Devices: TV, Fan
  • 2 Remotes: BasicRemote, AdvancedRemote

Now:

  1. You want to add a new deviceLight
  2. You want to add a new remoteGestureRemote

🔁 Comparison Table: Scalability Impact

ActionWithout Bridge (Tightly Coupled)With Bridge Pattern
📦 Classes you already haveBasicTVRemote, AdvancedTVRemote,
BasicFanRemote, AdvancedFanRemote
TV, Fan, BasicRemote, AdvancedRemote
➕ Add new device LightAdd:
BasicLightRemote, AdvancedLightRemote2 new classes
Add: Light only → 1 new class
➕ Add new remote GestureRemoteAdd:
GestureTVRemote, GestureFanRemote, GestureLightRemote3 new classes
Add: GestureRemote only → 1 new class
🧮 Total class count after both changes4 (existing) + 2 + 3 = 94 (existing) + 1 + 1 = 6

Imagine you have a remote control for a device (like a TV). The remote control doesn’t care what kind of device it’s controlling (TV, stereo, etc.), it just sends signals. The device itself doesn’t care what kind of remote control is being used (physical remote, app, etc.), it just receives signals and reacts accordingly.

The Bridge Design Pattern is like having a universal adapter between two separate things. These things can work with different partners as long as they speak the same “language” (interface). This allows for more flexibility and easier changes in the future.

Bridge Design Pattern is a structural design pattern that separates the abstraction (what needs to be done) from the implementation (how it’s done) by:

  1. Defining Interfaces: It creates interfaces (using abstract classes or pure virtual classes) that define the operations (functions) that concrete classes must implement. These interfaces act as the “language” that different parts of the code can understand.
  2. Separating Implementations: Concrete classes implement these interfaces, providing the actual functionality for the operations. This separation allows for independent development and modification of the abstraction and implementation.

class IDevice {
public:
    virtual ~IDevice() = default;
    virtual void on() = 0;
    virtual void off() = 0;
    virtual void volumeUp() = 0;
    virtual void volumeDown() = 0;
};

class IRemote {
    public :
        virtual ~IRemote() = default;
        virtual void press1() = 0;
        virtual void press2() = 0;
};

class TV : public IDevice {
public:
    void on();
    void off();
    void volumeUp() {}
    void volumeDown() {}  
};

class Radio : public IDevice {
public:
    void on();
    void off();
    void volumeUp() {}
    void volumeDown() {} 
};

class IRRemote : public IRemote {
    public:
        IRRemote(IDevice &device) : mDevice(device) {
            cout << "IR remote created for TV" <<endl;
        }
        virtual void press1();
        virtual void press2();
    private:
        IDevice & mDevice;
};

class BluetoothRemote : public IRemote {
    public:
        BluetoothRemote(IDevice &device) : mDevice(device) {
            cout << "bluetooth remote created for TV" <<endl;
        }
        virtual void press1();
        virtual void press2();
    private:
        IDevice & mDevice;
};

void TV::on() {
    cout <<" turn on TV" <<endl;
}

void TV::off() {
    cout <<" turn off TV" <<endl;
}

void Radio::on() {
    cout <<" turn on Radio" <<endl;
}

void Radio::off() {
    cout <<" turn off Radio" <<endl;
}

void IRRemote::press1() {
    cout <<" use ir remote ";
    mDevice.on();
}

void IRRemote::press2() {
    cout <<" use ir remote ";
    mDevice.off();
}

void BluetoothRemote::press1() {
    cout <<" use bt remote ";
    mDevice.on();
}

void BluetoothRemote::press2() {
    cout <<" use bt remote ";
    mDevice.off();
}

class UniversalRemote {
unique_ptr<IDevice> device;
unique_ptr<IRemote> remote;

public:
    UniversalRemote (unique_ptr<IDevice> &d, unique_ptr<IRemote> &r) : device(std::move(d)), remote (std::move(r)) {

    }

    void press1() {
       // cout <<" universal remote press1 " <<endl;
        remote->press1();
    }

    void press2() {
        //cout <<" universal remote press2 " <<endl;
        remote->press2();
    }

    void setRemote(unique_ptr<IRemote>& r) {
        cout <<" remote changed " << endl;
        remote = std::move(r);
    }
};

int main() {
    unique_ptr<IDevice> dev = make_unique<TV>();
    unique_ptr<IRemote> rem = make_unique<BluetoothRemote>(*dev);
    unique_ptr<IRemote> rem1 = make_unique<IRRemote>(*dev);

    UniversalRemote ur(dev, rem);
    ur.press1();
    ur.press2();


    ur.setRemote(rem1);
    ur.press1();
    ur.press2();
}

Follow more posts @ https://jdecodes.wordpress.com

My all design pattern articles :

It’s Jdecoder

I am trying to decode the concepts into simple words and documenting items i know or currently learning.

Let’s connect

Design a site like this with WordPress.com
Get started