Skip to content

Commit 711d9cc

Browse files
committed
[PROXY]: Added ressources and sample code for Proxy pattern.
1 parent c0e71d1 commit 711d9cc

File tree

3 files changed

+258
-2
lines changed

3 files changed

+258
-2
lines changed

structural-patterns/proxy/README.md

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,90 @@
1-
# TODO
1+
Also known as **Surrogate**.
22

3-
Stay tuned, this section will be completed shortly !
3+
# Intend
4+
5+
**Provide an interface to a particular ressource to control access to it**.
6+
7+
The ressource may be **remote**, **expensive to construct** or **require any kind of additional functionality** (such a logging for example).
8+
9+
Note : **The interface provided by the _Proxy_ should look exactly the same as the _Object_**.
10+
11+
Note : There are many different types of _Proxy_ :
12+
- Virtual proxy
13+
- Protection proxy
14+
- Remote proxy
15+
- ...
16+
17+
You should use the _Proxy_ pattern in the following cases :
18+
- You want to **add access restrictions to an object** (make it read-only, add a logging control operation...) (**Protection proxy**)
19+
- You have an **heavyweight service object that wastes system ressources** by being always up and you want to provide some kind of **Lazy initialisation** to that object (**Virtual proxy**)
20+
- You want to **provide local execution of a remote service** - Your service object is therefore located on a remote server. (**Remote proxy**)
21+
22+
# How it's done
23+
24+
![UML](UML.jpg)
25+
26+
**Participants**
27+
28+
- _Proxy_ :
29+
- **Maintains a reference** to the _RealSubject_ (proxied object).
30+
- **Provide an identical interface** to _Subject's_.
31+
- **Controls access** to the _RealSubject_.
32+
- _Subject_ : Degines the common interface of _RealSubject_ and _Proxy_ so that the _Proxy_ can be used anywhere the _RealSubject_ is needed.
33+
- _RealSubject_ : The real object represented by the _Proxy_.
34+
- _Client_ : Uses the _Proxy_ as if it was the _RealSubject_. The _Client_ might actually not even know about it.
35+
36+
**Notes about the _Proxy_ object**
37+
38+
- If the interface of both _Subject_ and _RealSubject_ are the same, then _Proxy_ might yield a reference to _Subject_ instead.
39+
- The _Proxy_ might also be responsible of the **Life cycle** of the _RealSubject_.
40+
- Additional responsabilities can depend of the kind of proxy :
41+
- **Remote proxy** : Encoding requests.
42+
- **Virtual proxy** : Caching additional informations about the _RealSubject_ so that they can postpone accessing it.
43+
- **Protection proxy** : Permissions checking.
44+
45+
**How to implement**
46+
47+
1. **Create** the _Proxy_ class with a field to a _RealSubject_ reference.
48+
The _Proxy_ might actually own the _RealSubject_ and be responsible of its creation/deletion, but not necessarily.
49+
2. **Implement** the _RealSubject_ interface (or _Subject's_ one, see the Notes above) into your _Proxy_ class. In most cases, a consequent part of the hard-work is delegated to the underlying _RealSubject_.
50+
3. Consider introducing a creation method that decides whether the client gets a proxy or a real service. (using a [**Factory**](../../creational-patterns/factory-method) for example).
51+
52+
Note : You also might want to use [**lazy initialization**](https://en.wikipedia.org/wiki/Lazy_initialization) for the service object.
53+
54+
Note : UML class diagram taken from [**here**](https://upload.wikimedia.org/wikipedia/commons/6/6e/W3sDesign_Proxy_Design_Pattern_UML.jpg)
55+
56+
# Pros & cons
57+
58+
**Pros**
59+
60+
- **Simplifies client** : Hide implementations details from clients.
61+
- **Open/Closed principle:** You can easily introduce new proxies without changing the service or client.
62+
63+
**Cons**
64+
65+
- **Complexity** : Increases the number of classes (as the majority of _Design patterns_).
66+
- The response from teh service might get delayed.
67+
- **Dependency inversion principle**: Usually, the _Proxy_ **has to know the concrete class** of the _RealSubject_.
68+
69+
# Notes
70+
71+
**Did you know ?**
72+
73+
- [_Smart pointers_](https://en.cppreference.com/w/cpp/memory) from the standard library is an example of the _Proxy_ design pattern.
74+
Indeed, consider the following code snippet :
75+
```
76+
aPointer->aFunction();
77+
*aPointer = aValue;
78+
```
79+
- There is no way you can know whether ```aPointer``` is a raw pointer or a smart pointer.
80+
- That is because _smart pointers_ provide an interface to access an underlying raw pointer that looks just like the interface of a raw pointer.
81+
82+
**Ressources**
83+
84+
Here are some _usefull ressources_ :
85+
- The [**w3sdesign**](http://w3sdesign.com/#gf) is really clear and concise !
86+
- A [**blog article**](http://www.vishalchovatiya.com/proxy-design-pattern-in-modern-cpp/) with examples of :
87+
- _Property Proxy_
88+
- _Virtual Proxy_
89+
- _Communication Proxy_
90+
- A [**Refactoring guru**](https://refactoring.guru/design-patterns/proxy) article.

structural-patterns/proxy/UML.jpg

35.8 KB
Loading

structural-patterns/proxy/proxy.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include <cstdint>
2+
#include <iostream>
3+
#include <string>
4+
#include <memory>
5+
#include <utility>
6+
7+
// ------------------- PROPERTY PROXY ------------------- //
8+
/*!
9+
* @brief : PROPERTY PROXY
10+
* Allow you to perform additional actions
11+
* (For example, log or intercept) on properties
12+
* access by a client without him seeing any difference.
13+
*/
14+
15+
template<typename T>
16+
class PropertyProxy {
17+
public:
18+
PropertyProxy(const T& p_val) : m_val(p_val) { }
19+
~PropertyProxy() = default;
20+
21+
// Allow conversion to T
22+
operator T() const { return m_val; }
23+
24+
// Allow conversion from T
25+
T operator= (const T& p_val) { return m_val = p_val; }
26+
27+
private:
28+
T m_val;
29+
};
30+
31+
class ClassWithProperties {
32+
public:
33+
ClassWithProperties (
34+
uint32_t p_prop1 = 0,
35+
uint32_t p_prop2 = 0 ) :
36+
m_prop1 (p_prop1),
37+
m_prop2 (p_prop2) {}
38+
~ClassWithProperties() = default;
39+
40+
public:
41+
PropertyProxy<uint32_t> m_prop1;
42+
PropertyProxy<uint32_t> m_prop2 ;
43+
};
44+
45+
// ------------------- VIRTUAL PROXY ------------------- //
46+
/*!
47+
* @brief : VIRTUAL PROXY
48+
* Allow you to perform lazy initializations
49+
* to not waste memory without the client to
50+
* even know about it.
51+
*/
52+
53+
class IPicture {
54+
public:
55+
IPicture(std::string p_fileName = ""): m_fileName(p_fileName) {}
56+
57+
virtual void draw(void) = 0;
58+
std::string getSrcFile(void) const { return m_fileName; }
59+
60+
protected:
61+
// Assuming images will always be loaded from a file
62+
std::string m_fileName;
63+
};
64+
65+
class JpgPicture : public IPicture {
66+
public:
67+
JpgPicture(std::string p_fileName = "") : IPicture(std::move(p_fileName))
68+
{
69+
/*
70+
* Actually perform the loading of the image eventho
71+
* we might never use it !
72+
*/
73+
std::cout << "\tLoading the JPEG picture from file " << m_fileName << std::endl;
74+
// Do the job
75+
// ...
76+
}
77+
78+
void draw(void) override
79+
{
80+
// All we have to do it to draw
81+
std::cout << "\tDrawing image loaded from " << m_fileName << std::endl;
82+
// Draw the image
83+
}
84+
};
85+
86+
class JpgLazyPicture : public IPicture {
87+
public:
88+
JpgLazyPicture(std::string p_fileName = "") : IPicture(std::move(p_fileName))
89+
{
90+
// We do not load the image yet since nobody asked for it yet...
91+
}
92+
93+
void draw(void) override
94+
{
95+
// We load the image now if this is the first request
96+
if ( !m_jpg ) { m_jpg = std::make_unique<JpgPicture>(m_fileName); }
97+
98+
// And then rely on JpgPicture to draw it
99+
m_jpg->draw();
100+
}
101+
102+
private:
103+
std::unique_ptr<JpgPicture> m_jpg;
104+
};
105+
106+
107+
// ------------------- COMMUNICATION PROXY ------------------- //
108+
/*!
109+
* @brief : COMMUNICATION PROXY
110+
* FROM http://www.vishalchovatiya.com/proxy-design-pattern-in-modern-cpp/#Property_Proxy
111+
*/
112+
113+
template <typename T>
114+
struct arr2D {
115+
struct proxy {
116+
proxy(T *arr) : m_arr_1D(arr) {}
117+
T &operator[](int32_t idx) {
118+
return m_arr_1D[idx];
119+
}
120+
121+
T *m_arr_1D;
122+
};
123+
124+
arr2D::proxy operator[](int32_t idx) {
125+
return arr2D::proxy(m_arr_2D[idx]);
126+
}
127+
128+
T m_arr_2D[10][10];
129+
};
130+
131+
/*!
132+
* @brief CLIENT CODE
133+
*/
134+
int main() {
135+
136+
// -------- Property proxy -------- //
137+
{
138+
/* The client will use the ClassWithProperties just
139+
* as if those properties actually were of their
140+
* base type (i.e. uint32_t)
141+
*/
142+
143+
ClassWithProperties anObject(10, 5);
144+
anObject.m_prop1 = 20;
145+
anObject.m_prop2 = 8;
146+
147+
std::cout << anObject.m_prop1 << std::endl;
148+
std::cout << anObject.m_prop2 << std::endl;
149+
}
150+
151+
// -------- Virtual proxy -------- //
152+
{
153+
JpgLazyPicture jpgLazy("myJpg.jpg");
154+
JpgPicture jpgBase("myJpg.jpg");
155+
156+
std::cout << "At this point, only jpgBase object has been loaded..." << std::endl;
157+
158+
std::cout << "Now let's ask for jpgLazy..." << std::endl;
159+
jpgLazy.draw();
160+
}
161+
162+
// ---- Communication proxy ---- //
163+
{
164+
arr2D<int32_t> arr;
165+
arr[0][0] = 1; // Uses the proxy object
166+
}
167+
168+
return 0;
169+
}

0 commit comments

Comments
 (0)