Definition
The bridge pattern intends to decouple an abstraction from its implementation so that the two can vary independently.
The bridge pattern is a bit like the adapter pattern, but the intention of using them is different:
Design pattern | Intention |
---|---|
Adapter pattern | We use it to hide the layer of calling other libraries, so that if the library changes, the error is constraint in a limited space. |
Bridge pattern | We use it to avoid the exponential growth in number of classes. (See scenario) |
Scenario
When we have a webpage that wants to display media sources like albums and books in a large view, a medium view, or a small view, we will need $2\times3=6$ kinds of class to pair up all of them. As the views and media sources grow, the number of classes grows exponentially.
But here, we can decouple the sources from the views and combine them using the bridge pattern, so that only $2+3=5$ of classes is needed.
Class Diagram
IView
defines the display methods, and it can have multiple views (ex: LargeView
returns html script for displaying in large view). The view class also holds media resource IResource*
, so that it can get the content.
IResource
is an interface that defines a group of methods for views to call. The methods are agnostic about when they will get called and what views are calling.
The subclasses of IResource
(ArtistResource
and BookResource
) have the actual media source (Artist
and Book
). Media sources (Artist
and Book
) are two independent classes with different methods and attributes. ArtistResource
and BookResource
have different media resources, but they all must have Snippet
and ImageUrl
defined in IResource
`. This follows the segregation principle, where a class only has methods for its purpose.
The bridge pattern enables different views ($m$) to pair up with different resources ($n$) and can have $m \times n$ of combinations. And this separates the platform-specific (IView
) from the platform-independent
(IResource
).
Used Cases
- When creating different views for different devices and different resources.
- When coupling different sets of objects.
Implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// bridge.h
#ifndef __BRIDGE_H__
#define __BRIDGE_H__
#include <memory>
#include "media.h" // contains Artist class and Book class
class IResource {
public:
virtual std::string Snippet() = 0;
virtual std::string ImageUrl() = 0;
};
class ArtistResource : public IResource {
private:
Artist artist_;
public:
// Another way is to pass in artist's pointer
ArtistResource(const std::string& name, const std::string& biograph) : artist_(name, biograph) {}
std::string Snippet() override;
std::string ImageUrl() override;
};
class IView {
protected:
std::unique_ptr<IResource> resource_;
public:
virtual void DisplayText() = 0;
virtual void DisplayImage() = 0;
};
class LargeView : public IView {
public:
LargeView(std::unique_ptr<IResource>&& resource);
void DisplayText() override;
void DisplayImage() override;
};
#endif //__BRIDGE_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// bridge.cpp
#include "bridge.h"
#include <iostream>
std::string ArtistResource::Snippet() {
std::string snippet = std::string("Name: ") + artist_.GetName() + std::string("\n");
snippet += std::string("Bio : ") + artist_.GetBiograph() + std::string("\n");
return snippet;
}
std::string ArtistResource::ImageUrl() {
return std::string(artist_.GetMediaUrl());
}
LargeView::LargeView(std::unique_ptr<IResource>&& resource) {
resource_ = std::move(resource);
}
void LargeView::DisplayText() {
std::cout << "<p>" << resource_->Snippet() << "</p>" << std::endl;
}
void LargeView::DisplayImage() {
std::cout << "<img src='" << resource_->ImageUrl() << ">" << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "bridge.h"
int main() {
LargeView large_view = LargeView(std::make_unique<ArtistResource>("cindytsai", "born in 1997"));
large_view.DisplayText();
large_view.DisplayImage();
return 0;
}