-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathencapsulation.d
508 lines (372 loc) · 13 KB
/
encapsulation.d
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
Ddoc
$(DERS_BOLUMU Encapsulation and Protection Attributes)
$(P
All of the structs and classes that we have defined so far have all been accessible from the $(I outside).
)
$(P
Let's consider the following struct:
)
---
enum Gender { female, male }
struct Student {
string name;
Gender gender;
}
---
$(P
The members of that struct is freely accessible to the rest of the program:
)
---
auto student = Student("Tim", Gender.male);
writefln("%s is a %s student.", student$(HILITE .name), student$(HILITE .gender));
---
$(P
Such freedom is a convenience in programs. For example, the previous line has been useful to produce the following output:
)
$(SHELL_SMALL
Tim is a male student.
)
$(P
However, this freedom is also a liability. As an example, let's assume that perhaps by mistake, the name of a student object gets modified in the program:
)
---
student.name = "Anna";
---
$(P
That assignment may put the object in an invalid state:
)
$(SHELL_SMALL
$(HILITE Anna) is a $(HILITE male) student.
)
$(P
As another example, let's consider a $(C School) class. Let's assume that this class has two member variables that store the numbers of the male and female students separately:
)
---
class School {
Student[] students;
size_t femaleCount;
size_t maleCount;
void add(in Student student) {
students ~= student;
final switch (student.gender) {
case Gender.female:
$(HILITE ++femaleCount);
break;
case Gender.male:
$(HILITE ++maleCount);
break;
}
}
override string toString() const {
return format("%s female, %s male; total %s students",
femaleCount, maleCount, students.length);
}
}
---
$(P
The $(C add()) member function adds students while ensuring that the counts are always correct:
)
---
auto school = new School;
school.add(Student("Lindsey", Gender.female));
school.add(Student("Mark", Gender.male));
writeln(school);
---
$(P
The program produces the following consistent output:
)
$(SHELL_SMALL
1 female, 1 male; total 2 students
)
$(P
However, being able to access the members of $(C School) freely would not guarantee that this consistency would always be maintained. Let's consider adding a new element to the $(C students) member, this time directly:
)
---
school$(HILITE .students) ~= Student("Nancy", Gender.female);
---
$(P
Because the new student has been added to the array directly, without going through the $(C add()) member function, the $(C School) object is now in an inconsistent state:
)
$(SHELL_SMALL
$(HILITE 1) female, $(HILITE 1) male; total $(HILITE 3) students
)
$(H5 $(IX encapsulation) Encapsulation)
$(P
Encapsulation is a programming concept of restricting access to members to avoid problems similar to the one above.
)
$(P
Another benefit of encapsulation is to eliminate the need to know the implementation details of types. In a sense, encapsulation allows presenting a type as a black box that is used only through its interface.
)
$(P
Additionally, preventing users from accessing the members directly allows changing the members of a class freely in the future. As long as the functions that define the interface of a class is kept the same, its implementation can be changed freely.
)
$(P
Encapsulation is not for restricting access to sensitive data like a credit card number or a password, and it cannot be used for that purpose. Encapsulation is a development tool: It allows using and coding types easily and safely.
)
$(H5 $(IX protection) $(IX access protection) Protection attributes)
$(P
Protection attributes limit access to members of structs, classes, and modules. There are two ways of specifying protection attributes:
)
$(UL
$(LI
At struct or class level to specify the protection of every struct or class member individually.
)
$(LI
At module level to specify the protection of every feature of a module individually: class, struct, function, enum, etc.
)
)
$(P
Protection attributes can be specified by the following keywords. The default attribute is $(C public).
)
$(UL
$(LI
$(IX public)
$(C public): Specifies accessibility by any part of the program without any restriction.
$(P
An example of this is $(C stdout). Merely importing $(C std.stdio) makes $(C stdout) available to every module that imported it.
)
)
$(LI
$(IX private)
$(C private): Specifies restricted accessibility.
$(P
$(C private) class members and module members can only be accessed by the module that defines that member.
)
$(P
Additionally, $(C private) member functions cannot be overridden by subclasses.
)
)
$(LI
$(IX package, protection)
$(C package): Specifies package-level accessibility.
$(P
A feature that is marked as $(C package) can be accessed by all of the code that is a part of the same package. The $(C package) attribute involves only the inner-most package.
)
$(P
For example, a $(C package) definition that is inside the $(C animal.vertebrate.cat) module can be accessed by any other module of the $(C vertebrate) package.
)
$(P
Similar to the $(C private) attribute, $(C package) member functions cannot be overridden by subclasses.
)
)
$(LI
$(IX protected)
$(C protected): Specifies accessibility by derived classes.
$(P
This attribute extends the $(C private) attribute: A $(C protected) member can be accessed not only by the module that defines it, but also by the classes that inherit from the class that defines that $(C protected) member.
)
)
)
$(P
$(IX export) Additionally, the $(C export) attribute specifies accessibility from the outside of the program.
)
$(H5 Definition)
$(P
Protection attributes can be specified in three ways.
)
$(P
When written in front of a single definition, it specifies the protection attribute of that definition only. This is similar to the Java programming language:
)
---
private int foo;
private void bar() {
// ...
}
---
$(P
When specified by a colon, it specifies the protection attributes of all of the following definitions until the next specification of a protection attribute. This is similar to the C++ programming language:
)
---
private:
// ...
// ... all of the definitions here are private ...
// ...
protected:
// ...
// ... all of the definitions here are protected ...
// ...
---
$(P
When specified for a block, the protection attribute is for all of the definitions that are inside that block:
)
---
private {
// ...
// ... all of the definitions here are private ...
// ...
}
---
$(H5 Module imports are private by default)
$(P
A module that is imported by $(C import) is private to the module that imports it. It would not be visible to other modules that import it indirectly. For example, if a $(C school) module imports $(C std.stdio), modules that import $(C school) cannot automatically use the $(C std.stdio) module.
)
$(P
Let's assume that the $(C school) module starts by the following lines:
)
---
module school.school;
import std.stdio; // imported for this module's own use...
// ...
---
$(P
The following program cannot be compiled because $(C writeln) is not visible to it:
)
---
import school.school;
void main() {
writeln("hello"); $(DERLEME_HATASI)
}
---
$(P
$(C std.stdio) must be imported by that module as well:
)
---
import school.school;
$(HILITE import std.stdio;)
void main() {
writeln("hello"); // now compiles
}
---
$(P
$(IX public import) $(IX import, public) Sometimes it is desired that a module presents other modules indirectly. For example, it would make sense for a $(C school) module to automatically import a $(C student) module for its users. This is achieved by marking the $(C import) as $(C public):
)
---
module school.school;
$(HILITE public import) school.student;
// ...
---
$(P
With that definition, modules that import $(C school) can use the definitions that are inside the $(C student) module without needing to import it:
)
---
import school.school;
void main() {
auto student = Student("Tim", Gender.male);
// ...
}
---
$(P
Although the program above imports only the $(C school) module, the $(C student.Student) struct is also available to it.
)
$(H5 When to use encapsulation)
$(P
Encapsulation avoids problems similar to the one we have seen in the introduction section of this chapter. It is an invaluable tool to ensure that objects are always in consistent states. Encapsulation helps preserve struct and class $(I invariants) by protecting members from direct modifications by the users of the type.
)
$(P
Encapsulation provides freedom of implementation by abstracting implementations away from user code. Otherwise, if users had direct access to for example $(C School.students), it would be hard to modify the design of the class by changing that array e.g. to an associative array, because this would affect all user code that has been accessing that member.
)
$(P
Encapsulation is one of the most powerful benefits of object oriented programming.
)
$(H5 Example)
$(P
Let's define the $(C Student) struct and the $(C School) class by taking advantage of encapsulation and let's use them in a short test program.
)
$(P
This example program will consist of three files. As you remember from the previous chapter, being parts of the $(C school) package, two of these files will be under the "school" directory:
)
$(UL
$(LI "school/student.d": The $(C student) module that defines the $(C Student) struct)
$(LI "school/school.d": The $(C school) module that defines the $(C School) class)
$(LI "deneme.d": A short test program)
)
$(P
Here is the "school/student.d" file:
)
---
module school.student;
import std.string;
import std.conv;
enum Gender { female, male }
struct Student {
$(HILITE package) string name;
$(HILITE package) Gender gender;
string toString() const {
return format("%s is a %s student.",
name, to!string(gender));
}
}
---
$(P
The members of this struct are marked as $(C package) to enable access only to modules of the same package. We will soon see that $(C School) will be accessing these members directly. (Note that even this should be considered as violating the principle of encapsulation. Still, let's stick with the $(C package) attribute in this example program.)
)
$(P
The following is the "school/school.d" module that makes use of the previous one:
)
---
module school.school;
$(HILITE public import) school.student; // 1
import std.string;
class School {
$(HILITE private:) // 2
Student[] students;
size_t femaleCount;
size_t maleCount;
$(HILITE public:) // 3
void add(in Student student) {
students ~= student;
final switch (student$(HILITE .gender)) { // 4a
case Gender.female:
++femaleCount;
break;
case Gender.male:
++maleCount;
break;
}
}
override string toString() const {
string result = format(
"%s female, %s male; total %s students",
femaleCount, maleCount, students.length);
foreach (i, student; students) {
result ~= (i == 0) ? ": " : ", ";
result ~= student$(HILITE .name); // 4b
}
return result;
}
}
---
$(OL
$(LI
$(C school.student) is being imported publicly so that the users of $(C school.school) will not need to import that module explicitly. In a sense, the $(C student) module is made available by the $(C school) module.
)
$(LI
All of the member variables of $(C School) are marked as private. This is important to help protect the consistency of the member variables of this class.
)
$(LI
For this class to be useful, it must present some member functions. $(C add()) and $(C toString()) are made available to the users of this class.
)
$(LI
As the two member variables of $(C Student) have been marked as $(C package), being a part of the same package, $(C School) can access those variables.
)
)
$(P
Finally, the following is a test program that uses those types:
)
---
import std.stdio;
import school.school;
void main() {
auto student = Student("Tim", Gender.male);
writeln(student);
auto school = new School;
school.add(Student("Lindsey", Gender.female));
school.add(Student("Mark", Gender.male));
school.add(Student("Nancy", Gender.female));
writeln(school);
}
---
$(P
This program can use $(C Student) and $(C School) only through their public interfaces. It cannot access the member variables of those types. As a result, the objects would always be consistent:
)
$(SHELL_SMALL
Tim is a male student.
2 female, 1 male; total 3 students: Lindsey, Mark, Nancy
)
$(P
Note that the program interacts with $(C School) only by its $(C add()) and $(C toString()) functions. As long as the interfaces of these functions are kept the same, changes in their implementations would not affect the program above.
)
Macros:
SUBTITLE=Encapsulation and Protection Attributes
DESCRIPTION=Encapsulating data by the protection attributes of D.
KEYWORDS=d programming lesson book tutorial encapsulation