-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathConceptualExample.cpp
More file actions
140 lines (120 loc) · 4.98 KB
/
Copy pathConceptualExample.cpp
File metadata and controls
140 lines (120 loc) · 4.98 KB
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// ===========================================================================
// ConceptualExample.cpp // Proxy Pattern
// ===========================================================================
#include <memory>
#include <print>
#include <utility>
namespace ConceptualExample_Proxy_Pattern {
/**
* The SubjectBase interface declares common operations for both 'RealSubject'
* and the 'Proxy'. As long as the client works with 'RealSubject'
* using this interface, you'll be able to pass it a proxy
* instead of a real subject.
*/
class SubjectBase
{
public:
virtual ~SubjectBase() = default;
virtual void request() const = 0;
};
/**
* The RealSubject contains some core business logic. Usually, RealSubjects are
* capable of doing some useful work which may also be very slow or sensitive -
* e.g. correcting input data. A Proxy can solve these issues without any
* changes to the RealSubject's code.
*/
class RealSubject final : public SubjectBase
{
public:
void request() const override {
std::println("RealSubject: Handling request.");
}
};
/**
* The Proxy has an interface identical to the RealSubject.
*
* Note: currently coupled specifically to RealSubject (not SubjectBase),
* since it needs to be able to construct it lazily. If you want the proxy
* to wrap arbitrary SubjectBase implementations, lazy-loading must be
* dropped (or a factory/std::function<std::unique_ptr<SubjectBase>()>
* injected instead). But normally a Proxy class is designed for a
* specific target class.
*/
class Proxy final : public SubjectBase
{
private:
// the proxy typically represents exactly one concrete type
mutable std::unique_ptr<RealSubject> m_realSubject;
// mutable because lazy initialization happens in a logically const operation
public:
/**
* The Proxy maintains a reference to an object of the RealSubject class.
* It can either be injected by the client, or left empty for lazy-loading
* (created on first request()).
*/
explicit Proxy(std::unique_ptr<RealSubject> subject = nullptr)
: m_realSubject{ std::move(subject) }
{}
void request() const override {
if (!checkAccess()) {
return;
}
/**
* True lazy-loading implementation:
* We create the heavy RealSubject object only when request() is called:
*/
if (!m_realSubject) {
std::println("Proxy: RealSubject is null - creating it now (Lazy Loading) ...");
m_realSubject = std::make_unique<RealSubject>();
}
/**
* The most common applications of the Proxy pattern are lazy loading,
* caching, controlling the access, logging, etc. A Proxy can perform one of
* these things and then, depending on the result, pass the execution to the
* same method in a linked RealSubject object.
*/
m_realSubject->request();
logAccess();
}
private:
[[nodiscard]]
bool checkAccess() const noexcept {
// some real checks should go here.
std::println("Proxy: Checking access prior to executing a real request.");
return true;
}
void logAccess() const noexcept {
std::println("Proxy: Logging the time of request.");
}
};
/**
* The client code is supposed to work with all objects (both subjects and
* proxies) via the SubjectBase interface in order to support both real subjects and proxies.
* In real life, however, clients mostly work with their real subjects
* directly. In this case, to implement the pattern more easily, you can extend
* your proxy from the real subject's class.
*/
static void clientCode(const SubjectBase& subject) {
subject.request();
}
}
void test_conceptual_example()
{
using namespace ConceptualExample_Proxy_Pattern;
std::println("Client: Executing the client code with a real subject:");
// no std::move, no forced heap allocation in the test code
RealSubject realSubject;
clientCode(realSubject);
std::println();
std::println("Client: Executing with a proxy (Injected Subject):");
auto dynamicRealSubject = std::make_unique<RealSubject>();
Proxy proxyWithInjection{ std::move(dynamicRealSubject) };
clientCode(proxyWithInjection);
std::println();
std::println("Client: Executing with a lazy proxy (No initial subject):");
Proxy lazyProxy; // RealSubject is not yet created!
clientCode(lazyProxy); // only here is the RealSubject created in the background.
}
// ===========================================================================
// End-of-File
// ===========================================================================