-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathGuidelinesInterfaces.cpp
More file actions
239 lines (190 loc) · 6.51 KB
/
GuidelinesInterfaces.cpp
File metadata and controls
239 lines (190 loc) · 6.51 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// ===========================================================================
// GuidelinesInterfaces.cpp // Recommendations for the design of interfaces
// ===========================================================================
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <print>
#include <span>
#include <stdexcept>
#include <string>
namespace GuidelinesInterfaces {
// =======================================================================
// Make interfaces precise
struct Point
{
int m_x;
int m_y;
};
struct Size
{
int m_height;
int m_width;
};
static void draw_rect(int, int, int, int) {}
static void draw_rectangle(Point top_left, Point bottom_right) {}
static void draw_rectangle(Point top_left, Size height_width) {}
static void guideline_precise_interface()
{
// Bad design: what do these four parameters mean? Great opportunities for making mistakes
// What do 10, 20 mean?
Point p{};
void draw_rect(int, int, int, int);
draw_rect(p.m_x, p.m_y, 10, 20);
// --------------------------------------------------
// Better design:
draw_rectangle(p, Point{ 10, 20 }); // two corners
draw_rectangle(p, Size{ 10, 20 }); // one corner and one size specification
}
// =======================================================================
// Preconditions
// State preconditions (if any)
static double sqrt(double x) {
//Expects(x >= 0); /* ... */
assert(x >= 0); // "x may not be negative"
return 0.0;
};
static void guideline_state_preconditions()
{
sqrt(-1.0);
}
// =======================================================================
// Virtual Base Class Destructor
class Base
{
public:
~Base() // <== add keyword 'virtual' in front of this line !!!
{
// do some important cleanup in class Base
std::println("d'tor Base");
}
// some virtual methods
virtual void doSomething() {}
};
class Derived : public Base
{
public:
~Derived()
{
// do some important cleanup in class Derived
std::println("d'tor Derived");
}
};
static void guideline_virtual_base_class_destructor()
{
Base* b = new Derived();
// use b
delete b; // here's the problem!
}
// =======================================================================
// Non-Virtual Interface (NVI) Pattern
class LoggerInterface {
public:
// 1. Virtual Destructor: Required for interfaces
virtual ~LoggerInterface() = default;
// 2. Public API (Non-virtual): This is where the pre/post conditions reside
void log(const std::string& message)
{
// Pre-Condition: Message must not be empty
assert(!message.empty() && "Log-Message must not be empty!");
// Call the actual logic
do_log(message);
// Post-Condition (Example): Increment counter or check status
std::println("[Audit: Log entry processed]");
}
protected:
// 3. Private/Protected Implementation (Pure Virtual)
// Derived classes only override this part.
virtual void do_log(const std::string& message) = 0;
};
// A Concrete Implementation
class ConsoleLogger : public LoggerInterface {
protected:
void do_log(const std::string& message) override
{
std::println("Console: {}", message);
}
};
static void guideline_non_virtual_interface_pattern()
{
ConsoleLogger myLogger;
LoggerInterface* interface = &myLogger;
// The call goes through the base class:
// Within the base class call, the overridden methods are dispatched
interface->log("System gestartet");
// interface->log(""); // Would trigger the assert
}
// =======================================================================
// Use exceptions
// Bad design: returns negative number if output fails.
// The return value can be ignored by the caller.
// Hint: Attribute [[nodiscard]] helps, but generates only a warning
static int someImportantMethod()
{
// ...
return -1;
}
// Good design:
// Use exceptions to signal a failure to perform a required task.
// The constructor of class std::thread throws std::system_error
// if the thread could not be started.
class thread
{
template <typename TFunc, typename ... TArgs>
explicit thread(TFunc&& func, TArgs&&... args) {};
};
static void guideline_use_exceptions()
{}
// =======================================================================
// Never transfer ownership by a raw pointer (T*)
class X {};
static X* compute(int args)
{
X* res = new X{};
// ...
return res;
}
static void guideline_transferring_ownership()
{
X* result = compute(123);
}
// =======================================================================
// Do not pass an array as a single pointer
static void copy_n(const int* from, int* to, int n)
{
for (int i = 0; i < n; ++i) {
to[i] = from[i];
}
}
static void copy(std::span<const int> from, std::span<int> to) {
// Safety check: Are both spans the same size?
if (from.size() != to.size()) {
throw std::invalid_argument("Spans must be the same size!");
}
// efficient data copying
std::copy(from.begin(), from.end(), to.begin());
}
static void guideline_passing_arrays()
{
int numbers[10]{};
int target[10]{};
copy_n(numbers, target, 10);
copy(std::span<int>{numbers}, std::span<int>{target});
}
}
// ===========================================================================
void test_guidelines_interfaces()
{
using namespace GuidelinesInterfaces;
guideline_precise_interface();
guideline_state_preconditions();
guideline_virtual_base_class_destructor();
guideline_non_virtual_interface_pattern();
guideline_use_exceptions();
guideline_transferring_ownership();
guideline_passing_arrays();
}
// ===========================================================================
// End-of-File
// ===========================================================================