Thorvas
Thorvas

Reputation: 73

Question about Bridge Pattern implementation

Recently, I've made several attempts to understand the Bridge Pattern. Different websites are trying to explain this concept in different ways, but I'm beginning to understand this pattern - decouple abstraction and implementation - allows us to make different kinds of implementations, and additionally, we are able to extend our interface. But I just want to make sure with one thing - based on the example below:

#include <iostream>

class Device
{
protected:
    int volume_m{ 0 };
public:
    int getVolume()
    {
        return volume_m;
    }
    void setVolume(int value)
    {
        volume_m = value;
    }
};

class RemoteController
{
private:
    std::shared_ptr<Device> device_m;
public:
    RemoteController(std::shared_ptr<Device> device) : device_m(device) {}
    void volumeUp()
    {
        device_m->setVolume(device_m->getVolume()+10);
        std::cout << "Volume turned up. Current volume: " << device_m->getVolume() << '\n';
    }
    void volumeDown()
    {
        this->device_m->setVolume(this->device_m->getVolume() - 10);
        std::cout << "Volume turned down. Current volume: " << device_m->getVolume() << '\n';
    }
    
};

class TV : public Device
{

};

class Radio : public Device
{

};


int main()
{
    std::shared_ptr<Device> tv = std::make_shared<TV>();
    std::shared_ptr<RemoteController> controller = std::make_shared<RemoteController>(tv);
    controller->volumeUp();
    controller->volumeUp();
    controller->volumeUp();
}

What if I wanted to make different messages for TV and Radio? Should I make virtual methods in Device called volumeUp() and volumeDown() which will be inherited by Radio and TV? And RemoteController would only call these virtual methods?

Upvotes: 1

Views: 288

Answers (2)

jfMR
jfMR

Reputation: 24738

Should I make virtual methods in Device called volumeUp() and volumeDown() which will be inherited by Radio and TV? And RemoteController would only call these virtual methods?

Yes, in short, The Bridge Pattern uses delegation. RemoteController delegates to the member functions defined by the Device interface. TV and Radio can override Device's virtual member functions.

The delegation is because The Bridge Pattern relies on composition. To understand why it does use composition, let's first see an example that doesn't take advantage of The Bridge Pattern.

Single Class Hierarchy Example – No Bridge

You have two different types of devices: TV and radio. Let's assume that you have two different remote controllers: radio frequency and infrared controllers. Then, you would have four classes in total: RadioIRController, RadioRFController, TVIRController, TVRFController. These classes would implement an interface RemoteController that specifies the virtual member functions volumeUp() and volumeDown(), i.e., a single hierarchy (i.e., RemoteController).

In this design, if you now wanted to add a new kind of remote controller, for example, an ultrasonic controller, you would have to create two additional classes: RadioUSController and TVUSController. If you, in turn, wanted to add a new kind of device, for example, a home stereo system, you would then need to create three additional classes: HomeStereoIRController, HomeStereoRFController and HomeStereoUSController.

As you can see, extending this design is tedious. This is because two orthogonal properties – the kind of device and remote controller – are mixed in a single class hierarchy. This is a strong motivation to switch to The Bridge Pattern.

single-hierarchy

Applying The Bridge Pattern

You split the single hierarchy into two: Controller and Device. This way, each hierarchy can be extended independently to each other.

bridge

We've done so by partially switching away from inheritance, embracing composition, and relying on delegation: a RemoteController refers to a Device and calls the operations on this Device object.

Extending this design for additionally supporting a home stereo system would only require to create a new class, HomeStereo, which inherits from Device. Likewise, extending this design for supporting an ultrasonic controller would just require the creation of a new class, USController, which inherits from RemoteController. You can now easily see the difference.

Upvotes: 0

alex_noname
alex_noname

Reputation: 32053

Yes, I believe that it will be more correct to implement VolumeUp VolumeDown methods in Radio and TV objects. Since they can potentially differ for these objects (not step 10 for both).

And I think that it’s better to try not to expose your implementation through getters and setters without much need. More about this here

Upvotes: 1

Related Questions