-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathch17-03-oo-design-patterns.html
784 lines (710 loc) · 95.4 KB
/
ch17-03-oo-design-patterns.html
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
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
<!DOCTYPE HTML>
<html lang="uk" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Реалізація Об'єктоорієнтованого Шаблону Проєктування - Мова програмування Rust</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="title-page.html">Мова Програмування Rust</a></li><li class="chapter-item expanded affix "><a href="foreword.html">Передмова</a></li><li class="chapter-item expanded affix "><a href="ch00-00-introduction.html">Вступ</a></li><li class="chapter-item expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Початок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Встановлення</a></li><li class="chapter-item expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="chapter-item expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Привіт, Cargo!</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Програмування Гри Відгадайки</a></li><li class="chapter-item expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Загальні Концепції Програмування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Змінні і Мутабельність</a></li><li class="chapter-item expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Типи Даних</a></li><li class="chapter-item expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Функції</a></li><li class="chapter-item expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Коментарі</a></li><li class="chapter-item expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Потік Виконання</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Розуміння Володіння</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> Що Таке Володіння?</a></li><li class="chapter-item expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> Посилання та Позичання</a></li><li class="chapter-item expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> Слайси</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Використання Структур для Групування Пов'язаних Даних</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Визначення та Створення Екземпляра Структури</a></li><li class="chapter-item expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> Приклад Програми з Використанням Структур</a></li><li class="chapter-item expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Синтаксис Методів</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Енуми та Зіставлення зі Шаблоном</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Визначення Енума</a></li><li class="chapter-item expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> Конструкція Потоку Виконання match</a></li><li class="chapter-item expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Лаконічний Потік Виконання з if let</a></li></ol></li><li class="chapter-item expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Керування Щораз Більшими Проєктами із Пакетами, Крейтами та Модулями</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Пакети та Крейти</a></li><li class="chapter-item expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Визначення Модулів для Контролю Області Видимості та Приватності</a></li><li class="chapter-item expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><strong aria-hidden="true">7.3.</strong> Шлях для Доступу до Елементів у Дереві Модулів</a></li><li class="chapter-item expanded "><a href="ch07-04-bringing-paths-into-scope-with-the-use-keyword.html"><strong aria-hidden="true">7.4.</strong> Введення Шляхів до Області Видимості з Ключовим Словом use</a></li><li class="chapter-item expanded "><a href="ch07-05-separating-modules-into-different-files.html"><strong aria-hidden="true">7.5.</strong> Розподіл Модулів на Різні Файли</a></li></ol></li><li class="chapter-item expanded "><a href="ch08-00-common-collections.html"><strong aria-hidden="true">8.</strong> Звичайні Колекції</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch08-01-vectors.html"><strong aria-hidden="true">8.1.</strong> Зберігання Списків Значень з Векторами</a></li><li class="chapter-item expanded "><a href="ch08-02-strings.html"><strong aria-hidden="true">8.2.</strong> Зберігання Тексту у Кодуванні UTF-8 в Стрічках</a></li><li class="chapter-item expanded "><a href="ch08-03-hash-maps.html"><strong aria-hidden="true">8.3.</strong> Зберігання Ключів з Асоційованими Значеннями у Хеш-Мапах</a></li></ol></li><li class="chapter-item expanded "><a href="ch09-00-error-handling.html"><strong aria-hidden="true">9.</strong> Обробка Помилок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong aria-hidden="true">9.1.</strong> Невідновлювані Помилки з panic!</a></li><li class="chapter-item expanded "><a href="ch09-02-recoverable-errors-with-result.html"><strong aria-hidden="true">9.2.</strong> Відновлювані Помилки з Result</a></li><li class="chapter-item expanded "><a href="ch09-03-to-panic-or-not-to-panic.html"><strong aria-hidden="true">9.3.</strong> panic! чи не panic!</a></li></ol></li><li class="chapter-item expanded "><a href="ch10-00-generics.html"><strong aria-hidden="true">10.</strong> Узагальнені Типи, Трейти та Часи Існування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch10-01-syntax.html"><strong aria-hidden="true">10.1.</strong> Узагальнені Типи Даних</a></li><li class="chapter-item expanded "><a href="ch10-02-traits.html"><strong aria-hidden="true">10.2.</strong> Трейти: Визначення Спільної Поведінки</a></li><li class="chapter-item expanded "><a href="ch10-03-lifetime-syntax.html"><strong aria-hidden="true">10.3.</strong> Перевірка Коректності Посилань із Часами Існування</a></li></ol></li><li class="chapter-item expanded "><a href="ch11-00-testing.html"><strong aria-hidden="true">11.</strong> Написання Автоматизованих Тестів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch11-01-writing-tests.html"><strong aria-hidden="true">11.1.</strong> Як Писати Тести</a></li><li class="chapter-item expanded "><a href="ch11-02-running-tests.html"><strong aria-hidden="true">11.2.</strong> Керування Запуском Тестів</a></li><li class="chapter-item expanded "><a href="ch11-03-test-organization.html"><strong aria-hidden="true">11.3.</strong> Організація Тестів</a></li></ol></li><li class="chapter-item expanded "><a href="ch12-00-an-io-project.html"><strong aria-hidden="true">12.</strong> Проєкт з Вводом/Виводом: Створення Програми з Інтерфейсом Командного Рядка</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch12-01-accepting-command-line-arguments.html"><strong aria-hidden="true">12.1.</strong> Приймання Аргументів Командного Рядка</a></li><li class="chapter-item expanded "><a href="ch12-02-reading-a-file.html"><strong aria-hidden="true">12.2.</strong> Читання Файлу</a></li><li class="chapter-item expanded "><a href="ch12-03-improving-error-handling-and-modularity.html"><strong aria-hidden="true">12.3.</strong> Рефакторинг для Покращення Модульності та Обробки Помилок</a></li><li class="chapter-item expanded "><a href="ch12-04-testing-the-librarys-functionality.html"><strong aria-hidden="true">12.4.</strong> Розробка Функціонала Бібліотеки із Test-Driven Development</a></li><li class="chapter-item expanded "><a href="ch12-05-working-with-environment-variables.html"><strong aria-hidden="true">12.5.</strong> Робота зі Змінними Середовища</a></li><li class="chapter-item expanded "><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong aria-hidden="true">12.6.</strong> Написання Повідомлень про Помилки у Помилковий Вивід замість Стандартного Виводу</a></li></ol></li><li class="chapter-item expanded "><a href="ch13-00-functional-features.html"><strong aria-hidden="true">13.</strong> Функціональні Можливості Мови: Ітератори та Замикання</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch13-01-closures.html"><strong aria-hidden="true">13.1.</strong> Замикання: Анонімні Функції, що Захоплюють Своє Середовище</a></li><li class="chapter-item expanded "><a href="ch13-02-iterators.html"><strong aria-hidden="true">13.2.</strong> Обробка Послідовностей Елементів з Ітераторами</a></li><li class="chapter-item expanded "><a href="ch13-03-improving-our-io-project.html"><strong aria-hidden="true">13.3.</strong> Покращення Нашого Проєкту з Вводом/Виводом</a></li><li class="chapter-item expanded "><a href="ch13-04-performance.html"><strong aria-hidden="true">13.4.</strong> Порівняння Швидкодії: Цикли Проти Ітераторів</a></li></ol></li><li class="chapter-item expanded "><a href="ch14-00-more-about-cargo.html"><strong aria-hidden="true">14.</strong> Більше про Cargo та Crates.io</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch14-01-release-profiles.html"><strong aria-hidden="true">14.1.</strong> Налаштування Збірок з Release Профілями</a></li><li class="chapter-item expanded "><a href="ch14-02-publishing-to-crates-io.html"><strong aria-hidden="true">14.2.</strong> Публікація Крейта на Crates.io</a></li><li class="chapter-item expanded "><a href="ch14-03-cargo-workspaces.html"><strong aria-hidden="true">14.3.</strong> Робочі Області Cargo</a></li><li class="chapter-item expanded "><a href="ch14-04-installing-binaries.html"><strong aria-hidden="true">14.4.</strong> Встановлення Двійкових Файлів з cargo install</a></li><li class="chapter-item expanded "><a href="ch14-05-extending-cargo.html"><strong aria-hidden="true">14.5.</strong> Розширення Cargo із Користувацькими Командами</a></li></ol></li><li class="chapter-item expanded "><a href="ch15-00-smart-pointers.html"><strong aria-hidden="true">15.</strong> Розумні Вказівники</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch15-01-box.html"><strong aria-hidden="true">15.1.</strong> Використання Box<T> для Вказування на Дані в Купі</a></li><li class="chapter-item expanded "><a href="ch15-02-deref.html"><strong aria-hidden="true">15.2.</strong> Ставлення до Розумних Вказівників як до Звичайних Посилань з Трейтом Deref</a></li><li class="chapter-item expanded "><a href="ch15-03-drop.html"><strong aria-hidden="true">15.3.</strong> Виконання Коду при Очищенні з Трейтом Drop</a></li><li class="chapter-item expanded "><a href="ch15-04-rc.html"><strong aria-hidden="true">15.4.</strong> Rc<T> - Розумний Вказівник з Лічильником Посилань</a></li><li class="chapter-item expanded "><a href="ch15-05-interior-mutability.html"><strong aria-hidden="true">15.5.</strong> RefCell<T> та Шаблон Внутрішньої Мутабельності</a></li><li class="chapter-item expanded "><a href="ch15-06-reference-cycles.html"><strong aria-hidden="true">15.6.</strong> Цикли Посилань Можуть Спричинити Витік Пам'яті</a></li></ol></li><li class="chapter-item expanded "><a href="ch16-00-concurrency.html"><strong aria-hidden="true">16.</strong> Безстрашна Конкурентність</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch16-01-threads.html"><strong aria-hidden="true">16.1.</strong> Використання Потоків для Одночасного Виконання Коду</a></li><li class="chapter-item expanded "><a href="ch16-02-message-passing.html"><strong aria-hidden="true">16.2.</strong> Застосування Обміну Повідомлень для Передавання Даних між Потоками</a></li><li class="chapter-item expanded "><a href="ch16-03-shared-state.html"><strong aria-hidden="true">16.3.</strong> Конкурентність зі Спільним Станом</a></li><li class="chapter-item expanded "><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong aria-hidden="true">16.4.</strong> Розширювана Конкурентність із Трейтами Sync та Send</a></li></ol></li><li class="chapter-item expanded "><a href="ch17-00-oop.html"><strong aria-hidden="true">17.</strong> Особливості Об'єктоорієнтованого Програмування в Rust</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch17-01-what-is-oo.html"><strong aria-hidden="true">17.1.</strong> Характеристики Об'єктоорієнтованих Мов</a></li><li class="chapter-item expanded "><a href="ch17-02-trait-objects.html"><strong aria-hidden="true">17.2.</strong> Використання Трейт-Об'єктів, які Допускають Значення Різних Типів</a></li><li class="chapter-item expanded "><a href="ch17-03-oo-design-patterns.html" class="active"><strong aria-hidden="true">17.3.</strong> Реалізація Об'єктоорієнтованого Шаблону Проєктування</a></li></ol></li><li class="chapter-item expanded "><a href="ch18-00-patterns.html"><strong aria-hidden="true">18.</strong> Шаблони та Зіставлення Шаблонів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch18-01-all-the-places-for-patterns.html"><strong aria-hidden="true">18.1.</strong> Усі Місця Можливого Використання Шаблонів</a></li><li class="chapter-item expanded "><a href="ch18-02-refutability.html"><strong aria-hidden="true">18.2.</strong> Спростовуваність: Чи Може Шаблон Бути Невідповідним</a></li><li class="chapter-item expanded "><a href="ch18-03-pattern-syntax.html"><strong aria-hidden="true">18.3.</strong> Синтаксис Шаблонів</a></li></ol></li><li class="chapter-item expanded "><a href="ch19-00-advanced-features.html"><strong aria-hidden="true">19.</strong> Просунуті Можливості</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch19-01-unsafe-rust.html"><strong aria-hidden="true">19.1.</strong> Небезпечний Rust</a></li><li class="chapter-item expanded "><a href="ch19-03-advanced-traits.html"><strong aria-hidden="true">19.2.</strong> Поглиблено про Трейти</a></li><li class="chapter-item expanded "><a href="ch19-04-advanced-types.html"><strong aria-hidden="true">19.3.</strong> Поглиблено про Типи</a></li><li class="chapter-item expanded "><a href="ch19-05-advanced-functions-and-closures.html"><strong aria-hidden="true">19.4.</strong> Поглиблено про Функції та Замикання</a></li><li class="chapter-item expanded "><a href="ch19-06-macros.html"><strong aria-hidden="true">19.5.</strong> Макроси</a></li></ol></li><li class="chapter-item expanded "><a href="ch20-00-final-project-a-web-server.html"><strong aria-hidden="true">20.</strong> Останній Проєкт: Збірка Багатопотокового Вебсервера</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch20-01-single-threaded.html"><strong aria-hidden="true">20.1.</strong> Збірка Однопотокового Вебсервера</a></li><li class="chapter-item expanded "><a href="ch20-02-multithreaded.html"><strong aria-hidden="true">20.2.</strong> Перетворюємо Наш Однопотоковий Сервер на Багатопотоковий</a></li><li class="chapter-item expanded "><a href="ch20-03-graceful-shutdown-and-cleanup.html"><strong aria-hidden="true">20.3.</strong> Плавне Вимкнення та Очищення</a></li></ol></li><li class="chapter-item expanded "><a href="appendix-00.html"><strong aria-hidden="true">21.</strong> Додатки</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="appendix-01-keywords.html"><strong aria-hidden="true">21.1.</strong> A - Ключові Слова</a></li><li class="chapter-item expanded "><a href="appendix-02-operators.html"><strong aria-hidden="true">21.2.</strong> B - Оператори та Символи</a></li><li class="chapter-item expanded "><a href="appendix-03-derivable-traits.html"><strong aria-hidden="true">21.3.</strong> C - Похідні Трейти</a></li><li class="chapter-item expanded "><a href="appendix-04-useful-development-tools.html"><strong aria-hidden="true">21.4.</strong> D - Корисні Інструменти Розробки</a></li><li class="chapter-item expanded "><a href="appendix-05-editions.html"><strong aria-hidden="true">21.5.</strong> E - Видання</a></li><li class="chapter-item expanded "><a href="appendix-06-translation.html"><strong aria-hidden="true">21.6.</strong> F - Переклади Книги</a></li><li class="chapter-item expanded "><a href="appendix-07-nightly-rust.html"><strong aria-hidden="true">21.7.</strong> G - як Розробляється Rust і "Нічний Rust"</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Мова програмування Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="Реалізація-Обєктоорієнтованого-Шаблону-Проєктування"><a class="header" href="#Реалізація-Обєктоорієнтованого-Шаблону-Проєктування">Реалізація Об'єктоорієнтованого Шаблону Проєктування</a></h2>
<p>Шаблон <em>"Стан"</em> - це об'єктоорієнтований шаблон проєктування. Сенс патерну полягає в тому, що ми визначаємо набір станів, в яких може знаходитися значення. Стани представлені набором <em>об'єктів стану</em>, а поведінка значення змінюється в залежності від його стану. Розглянемо на прикладі структури допису в блозі, що має поле для збереження її стану, яке буде об'єктом стану з набору "чернетка" (draft), "очікування перевірки" (review) або "опубліковано" (published).</p>
<p>Об'єкти стану мають спільну функціональність: звісно в Rust, ми використовуємо структури й трейти, а не об'єкти та наслідування. Кожний об'єкт стану відповідає за свою поведінку й сам визначає, коли він повинен перейти в інший стан. Значення, яке зберігає об'єкт стану, нічого не знає про різницю в поведінці станів або про те, коли один стан повинен перейти в інший.</p>
<p>Перевага використання патерну "Стан" полягає в тому, що при зміненні бізнес-вимог до програми нам не потрібно буде змінювати код значення, що зберігає стан, або код, який використовує це значення. Нам потрібно буде оновити код всередині одного з об’єктів стану, щоб змінити його правила чи можливо додати більше об'єктів стану.</p>
<p>Спочатку, ми реалізуємо шаблон "Стан" більш традиційним об'єктоорієнтованим способом, а потім використаємо більш ідіоматичний для Rust підхід. Розглянемо поетапну реалізацію робочого процесу публікації в блозі з використанням патерну "Стан".</p>
<p>Остаточна функціональність буде виглядати наступним чином:</p>
<ol>
<li>Створення допису в блозі починається з пустої чернетки.</li>
<li>Коли чернетка готова, робиться запит на схвалення допису.</li>
<li>Коли допис буде схвалено, він опублікується.</li>
<li>Тільки опубліковані дописи блогу повертають контент для друку, тому несхвалені дописи не можуть випадково бути опубліковані.</li>
</ol>
<p>Будь-які інші зміни, зроблені в дописі, не повинні мати ефекту. Наприклад, якщо ми спробуємо затвердити чернетку допису в блозі перед тим, як ми подали запит на затвердження, допис має залишатися неопублікованою чернеткою.</p>
<p>Лістинг 17-11 показує цей процес у вигляді коду: це приклад використання API (прикладного програмного інтерфейсу), який ми будемо впроваджувати у бібліотечному крейті під назвою <code>blog</code>. Цей приклад не скомпілюється, тому що ми ще не встигли реалізувати крейт <code>blog</code>.</p>
<p><span class="filename">Файл: src/main.rs</span></p>
<pre><code class="language-rust ignore does_not_compile">use blog::Post;
fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
post.request_review();
assert_eq!("", post.content());
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
</code></pre>
<p><span class="caption">Блок коду 17-11: Код, який демонструє поведінку, яку ми хочемо, щоб мав крейт <code>blog</code></span></p>
<p>Ми хочемо дозволити користувачеві створити новий допис у блозі за допомогою <code>Post::new</code>. Ми хочемо дозволити додавати текст у допис блогу. Якщо ми спробуємо отримати зміст допису до схвалення публікації, ми не повинні отримувати ніякого тексту, оскільки допис все ще є чернеткою. Ми додали <code>assert_eq!</code> в коді для демонстрації цілей. Ідеальним модульним (unit) тестом для цього було б твердження, що чернетка допису повертає порожній рядок з методу <code>content</code>, але ми не будемо писати тести для цього прикладу.</p>
<p>Далі ми хочемо дозволити запит на схвалення допису, і також щоб <code>content</code> повертав порожню стрічку під час очікування схвалення. Коли допис пройде перевірку, він повинен бути опублікований, тобто виклик методу <code>content</code> буде повертати текст допису.</p>
<p>Зверніть увагу, що єдиний тип з крейту, з яким ми взаємодіємо - це тип <code>Post</code>. Цей тип буде використовувати патерн "Стан" і буде містить значення, яке буде одним з трьох об'єктів станів, які представляють різні стани, в яких може знаходитися допис: "чернетка", "очікування перевірки", або "опубліковано". Керування переходом з одного стану в інший буде здійснюватися внутрішньою логікою типа <code>Post</code>. Стани будуть перемикатися в результаті реакції на виклик методів екземпляру <code>Post</code> користувачами нашої бібліотеки, але користувачі не повинні керувати зміною станів напряму. Крім того, користувачі не повинні мати можливість помилитися зі станами, наприклад, опублікувати повідомлення до його перевірки.</p>
<h3 id="Визначення-post-і-Створення-Нового-Екземпляру-в-Стані-Чернетки"><a class="header" href="#Визначення-post-і-Створення-Нового-Екземпляру-в-Стані-Чернетки">Визначення <code>Post</code> і Створення Нового Екземпляру в Стані Чернетки</a></h3>
<p>Нумо почнімо реалізовувати бібліотеку! Ми знаємо, що нам потрібна публічна структура <code>Post</code>, яка зберігає деякий вміст, тому ми почнемо з визначення структури та пов'язаною з нею публічною функцією <code>new</code> для створення екземпляра <code>Post</code>, як показано в Блоці коду 17-12. Ми також зробимо приватний трейт <code>State</code>, який буде визначати поведінку, що повинні будуть мати всі об'єкти станів структури <code>Post</code>.</p>
<p>Далі <code>Post</code> буде містити трейт-об'єкт <code>Box<dyn State></code> всередині <code>Option<T></code> в приватному полі <code>state</code> для зберігання об'єкту стану. Трохи пізніше ви зрозумієте, навіщо потрібно використання <code>Option<T></code>.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">pub struct Post {
state: Option<Box<dyn State>>,
content: String,
}
impl Post {
pub fn new() -> Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
}
trait State {}
struct Draft {}
impl State for Draft {}
</code></pre>
<p><span class="caption">Блок коду 17-12. Визначення структури <code>Post</code> та функції <code>new</code>, яка створює новий екземпляр <code>Post</code>, трейту <code>State</code> і структури <code>Draft</code></span></p>
<p>Трейт <code>State</code> визначає поведінку, яку спільно використовують різні стани допису. Всі об'єкти станів (<code>Draft</code> - чернетка, <code>PendingReview</code> - очікування перевірки, <code>Published</code> - опубліковано) будуть реалізовувати трейт <code>State</code>. Зараз у цього трейту немає ніяких методів, і ми почнемо з визначення <code>Draft</code>, тому що, що це перший стан, з якого, як ми хочемо, публікація буде починати свій шлях.</p>
<p>Коли ми створюємо новий екземпляр <code>Post</code>, ми встановлюємо його поле <code>state</code> в значення <code>Some</code>, що містить <code>Box</code>. Цей <code>Box</code> вказує на новий екземпляр структури <code>Draft</code>. Це гарантує, щоразу, коли ми створюємо новий екземпляр <code>Post</code>, він з'явиться як чернетка. Оскільки поле <code>state</code> в структурі <code>Post</code> є приватним, нема ніякого способу створити <code>Post</code> в якомусь іншому стані! У функції <code>Post::new</code> ми ініціалізуємо поле <code>content</code> новим пустим рядком типу <code>String</code>.</p>
<h3 id="Зберігання-Тексту-Вмісту-Публікації"><a class="header" href="#Зберігання-Тексту-Вмісту-Публікації">Зберігання Тексту Вмісту Публікації</a></h3>
<p>У Блоці коду 17-11 показано, що ми хочемо мати можливість викликати метод <code>add_text</code> і передати йому <code>&str</code>, яке додається до текстового вмісту допису блогу. Ми реалізуємо цю можливість як метод, а не робимо поле <code>content</code> публічним, використовуючи <code>pub</code>, щоб пізніше ми могли реалізувати метод, який буде керувати тим, як дані поля <code>content</code> будуть зчитуватися. Метод <code>add_text</code> досить простий, тому додаймо його реалізацію в блок <code>impl Post</code> у Блоці коду 17-13:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
}
<span class="boring">
</span><span class="boring">trait State {}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {}
</span></code></pre>
<p><span class="caption">Блок коду 17-13. Реалізація методу <code>add_text</code> для додавання тексту до <code>content</code> (вмісту) допису</span></p>
<p>Метод <code>add_text</code> приймає змінюване посилання на <code>self</code>, тому що ми змінюємо екземпляр <code>Post</code>, для якого викликаємо <code>add_text</code>. Потім ми викликаємо <code>push_str</code> для <code>String</code> у поля <code>content</code> і передаємо <code>text</code> аргументом для додавання до збереженого <code>content</code>. Ця поведінка не залежить від стану, в якому знаходяться допис, таким чином він не є частиною патерну "Стан". Метод <code>add_text</code> взагалі не взаємодіє з полем <code>state</code>, але це частина поведінки, яку ми хочемо підтримувати.</p>
<h3 id="Забезпечення-Відсутності-Вмісту-Чернетки-Публікації"><a class="header" href="#Забезпечення-Відсутності-Вмісту-Чернетки-Публікації">Забезпечення Відсутності Вмісту Чернетки Публікації</a></h3>
<p>Навіть після того, як ми викликали метод <code>add_text</code> і додали деякий контент в наш допис, ми хочемо, щоб метод <code>content</code> повертав порожній стрічковий слайс, тому, що допис все ще знаходиться в стані чернетки, як це показано в рядку 7 Блока коду 17-11. Поки що реалізуймо метод <code>content</code> найпростішим способом, який буде задовольняти цій вимозі: будемо завжди повертати порожній стрічковий слайс. Ми змінимо код пізніше, як тільки реалізуємо можливість змінити стан допису, щоб вона могла бути опублікована. Поки що дописи можуть знаходитися тільки в стані чернетки, тому вміст допису завжди повинен бути пустим. Лістинг 17-14 показує цю реалізацію-заглушку:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn content(&self) -> &str {
""
}
}
<span class="boring">
</span><span class="boring">trait State {}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {}
</span></code></pre>
<p><span class="caption">Блок коду 17-14: Додавання реалізації-заглушки для методу <code>content</code> в <code>Post</code>, яка завжди повертає порожній стрічковий слайс</span></p>
<p>З доданим методом <code>content</code> усе в Блоці коду 17-11 працює, як треба, аж до рядка 7.</p>
<h3 id="Запит-на-Перевірку-Публікації-Змінює-Її-Стан"><a class="header" href="#Запит-на-Перевірку-Публікації-Змінює-Її-Стан">Запит на Перевірку Публікації Змінює Її Стан</a></h3>
<p>Далі нам потрібно додати функціональність для запиту на перевірку допису, який повинен змінити її стан з <code>Draft</code> на <code>PendingReview</code>. Лістинг 17-15 показує такий код:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&self) -> &str {
</span><span class="boring"> ""
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}
}
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
}
struct Draft {}
impl State for Draft {
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview {})
}
}
struct PendingReview {}
impl State for PendingReview {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
}
</code></pre>
<p><span class="caption">Блок коду 17-15: Реалізація методу <code>request_review</code> в структурі <code>Post</code> і трейті <code>State</code></span></p>
<p>Ми додаємо в <code>Post</code> публічний метод з іменем <code>request_review</code>, який буде приймати змінюване посилання на <code>self</code>. Далі ми викликаємо внутрішній метод <code>request_review</code> для поточного стану <code>Post</code>, і цей другий метод <code>request_review</code> поглинає поточний стан та повертає новий стан.</p>
<p>Ми додаємо метод <code>request_review</code> в трейт <code>State</code>; всі типи, які реалізують цей трейт, тепер повинні будуть реалізувати метод <code>request_review</code>. Зверніть увагу, що замість <code>self</code>, <code>&self</code>, або <code>&mut self</code> як першого параметра метода в нас вказаний <code>self: Box<Self></code>. Цей синтаксис означає, що метод дійсний тільки при його виклику з обгорткою <code>Box</code>, яка містить наш тип. Цей синтаксис стає власником <code>Box<Self></code>, і робить старий стан недійсним, тому значення стану <code>Post</code> може бути перетворення в новий стан.</p>
<p>Щоб поглинути старий стан, метод <code>request_review</code> повинен стати власником значення стану. Це місце, де приходить на допомогу тип <code>Option</code> поля <code>state</code> допису <code>Post</code>: ми викликаємо метод <code>take</code>, щоб забрати значення <code>Some</code> з поля <code>state</code> і залишити замість нього значення <code>None</code>, тому що Rust не дозволяє мати неініціалізовані поля в структурах. Це дозволяє переміщувати значення <code>state</code> з <code>Post</code>, а не запозичувати його. Потім ми встановимо нове значення <code>state</code> як результат цієї операції.</p>
<p>Нам потрібно тимчасово встановити <code>state</code> в <code>None</code> замість того, щоб встановити його напряму за допомогою коду на кшталт <code>self.state = self.state.request_review();</code> щоб отримати власність над значенням <code>state</code>. Це гарантує, що <code>Post</code> не зможе використовувати старе значення <code>state</code> після того, як ми перетворили його в новий стан.</p>
<p>Метод <code>request_review</code> в <code>Draft</code> повинен повернути екземпляр нової структури <code>PendingReview</code> обгорнутої в <code>Box</code>, яка є станом, коли допис очікує на перевірку. Структура <code>PendingReview</code> також реалізує метод <code>request_review</code>, але не виконує ніяких трансформацій. Вона повертає сама себе, тому що, коли ми робимо запит на перевірку допису, який вже знаходиться в стані <code>PendingReview</code>, вона все одно повинна продовжувати залишатися в стані <code>PendingReview</code>.</p>
<p>Тепер ми починаємо бачити переваги патерну "Стан": метод <code>request_review</code> для <code>Post</code> однаковий, він не залежить від значення <code>state</code>. Кожен стан сам несе відповідальність за власну поведінку.</p>
<p>Залишимо метод <code>content</code> в <code>Post</code> без змін, тобто який повертає порожній стрічковий слайс. Тепер ми можемо мати <code>Post</code> як у стані <code>PendingReview</code>, так і в стані <code>Draft</code>, але ми хочемо отримати таку саму поведінку в стані <code>PendingReview</code>. Лістинг 17-11 тепер працює до рядка 10!</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="adding-the-approve-method-that-changes-the-behavior-of-content"></a></p>
<h3 id="Додавання-Методу-approve-для-Зміни-Поведінки-Методу-content"><a class="header" href="#Додавання-Методу-approve-для-Зміни-Поведінки-Методу-content">Додавання Методу <code>approve</code> для Зміни Поведінки Методу <code>content</code></a></h3>
<p>Метод <code>approve</code> ("схвалити") буде аналогічним методу <code>request_review</code>: він буде встановлювати в <code>state</code> значення, яке повинен мати допис при його схваленні, як показано в Блоці коду 17-16:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&self) -> &str {
</span><span class="boring"> ""
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn request_review(&mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn approve(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.approve())
}
}
}
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
fn approve(self: Box<Self>) -> Box<dyn State>;
}
struct Draft {}
impl State for Draft {
// --snip--
<span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span> fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
}
struct PendingReview {}
impl State for PendingReview {
// --snip--
<span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span> fn approve(self: Box<Self>) -> Box<dyn State> {
Box::new(Published {})
}
}
struct Published {}
impl State for Published {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
}
</code></pre>
<p><span class="caption">Блок коду 17-16: Реалізація методу <code>approve</code> для типу <code>Post</code> і трейту <code>State</code></span></p>
<p>Ми додаємо метод <code>approve</code> в трейт <code>State</code> та додаємо нову структуру, яка реалізує трейт <code>State</code> для стану <code>Published</code>.</p>
<p>Подібно до того, як працює метод <code>request_review</code> для <code>PendingReview</code>, якщо ми викличемо метод <code>approve</code> для <code>Draft</code>, це не буде мати ніякого ефекту, тому що <code>approve</code> поверне <code>self</code>. Коли ми викликаємо метод <code>approve</code> для <code>PendingReview</code>, він повертає новий, обгорнутий у <code>Box</code>, екземпляр структури <code>Published</code>. Структура <code>Published</code> реалізує трейт <code>State</code>, і як для методу <code>request_review</code>, так і для методу <code>approve</code> вона повертає себе, тому що в цих випадках допис повинен залишатися в стані <code>Published</code>.</p>
<p>Тепер нам потрібно оновити метод <code>content</code> для <code>Post</code>. Ми хочемо, щоб значення, яке повертається з <code>content</code>, залежало від поточного стану <code>Post</code>, тому ми збираємося делегувати частину функціональності <code>Post</code> в метод <code>content</code>, визначений для <code>state</code>, як показано в Блоці коду 17-17:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn content(&self) -> &str {
self.state.as_ref().unwrap().content(self)
}
// --snip--
<span class="boring">
</span><span class="boring"> pub fn request_review(&mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn approve(&mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.approve())
</span><span class="boring"> }
</span><span class="boring"> }
</span>}
<span class="boring">
</span><span class="boring">trait State {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State>;
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State>;
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct PendingReview {}
</span><span class="boring">
</span><span class="boring">impl State for PendingReview {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> Box::new(Published {})
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct Published {}
</span><span class="boring">
</span><span class="boring">impl State for Published {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p><span class="caption">Блок коду 17-17: Оновлення методу <code>content</code> в структурі <code>Post</code> для делегування частини функціональності методу <code>content</code> структури <code>State</code></span></p>
<p>Оскільки наша ціль полягає в тому, щоб зберегти ці дії всередині структур, які реалізують трейт <code>State</code>, ми викликаємо метод <code>content</code> у значення в полі <code>state</code> і передаємо екземпляр публікації (тобто <code>self</code>) як аргумент. Потім ми повертаємо значення, яке нам повертає виклик методу <code>content</code> поля <code>state</code>.</p>
<p>Ми викликаємо метод <code>as_ref</code> у <code>Option</code>, тому що нам потрібне посилання на значення всередині <code>Option</code>, а не володіння значенням. Оскільки <code>state</code> є типом <code>Option<Box<dyn State>></code>, то під час виклику методу <code>as_ref</code> повертається <code>Option<&Box<dyn State>></code>. Якби ми не викликали <code>as_ref</code>, отримали б помилку, тому що ми не можемо перемістити <code>state</code> з запозиченого параметра <code>&self</code> функції.</p>
<p>Далі ми викликаємо метод <code>unwrap</code>. Ми знаємо, що цей метод тут ніколи не призведе до аварійного завершення програми, бо всі методи <code>Post</code> влаштовані таким чином, що після їх виконання, в поле <code>state</code> завжди міститься значення <code>Some</code>. Це один з випадків, про яких ми говорили в розділі <a href="ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler">"Випадки, коли у вас більше інформації, ніж у компілятора"</a><!-- ignore --> розділу 9 - випадок, коли ми знаємо, що значення <code>None</code> ніколи не зустрінеться, навіть якщо компілятор не може цього зрозуміти.</p>
<p>Тепер, коли ми викликаємо <code>content</code> у <code>&Box<dyn State></code>, в дію вступає перетворення під час розіменування (deref coercion) для <code>&</code> та <code>Box</code>, тому в підсумку метод <code>content</code> буде викликаний для типу, який реалізує трейт <code>State</code>. Це означає, що нам потрібно додати метод <code>content</code> у визначення трейту <code>State</code>, і саме там ми розмістимо логіку для з'ясування того, який вміст повертати, в залежності від поточного стану, як показано в Блоці коду 17-18:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option<Box<dyn State>>,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Post {
</span><span class="boring"> pub fn new() -> Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&self) -> &str {
</span><span class="boring"> self.state.as_ref().unwrap().content(self)
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn request_review(&mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn approve(&mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.approve())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>trait State {
// --snip--
<span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State>;
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State>;
</span><span class="boring">
</span> fn content<'a>(&self, post: &'a Post) -> &'a str {
""
}
}
// --snip--
<span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct PendingReview {}
</span><span class="boring">
</span><span class="boring">impl State for PendingReview {
</span><span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> Box::new(Published {})
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>struct Published {}
impl State for Published {
// --snip--
<span class="boring"> fn request_review(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box<Self>) -> Box<dyn State> {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span> fn content<'a>(&self, post: &'a Post) -> &'a str {
&post.content
}
}
</code></pre>
<p><span class="caption">Блок коду 17-18: Додавання методу <code>content</code> в трейт <code>State</code></span></p>
<p>Ми додаємо реалізацію за замовчуванням метода <code>content</code>, який повертає порожній стрічковий слайс. Це означає, що нам не прийдеться реалізовувати <code>content</code> в структурах <code>Draft</code> та <code>PendingReview</code>. Структура <code>Published</code> буде перевизначати метод <code>content</code> та поверне значення з <code>post.content</code>.</p>
<p>Зверніть увагу, що для цього метода нам потрібні анотації часу життя, як ми обговорювали в розділі 10. Ми беремо посилання на <code>post</code> як аргумент та повертаємо посилання на частину цього <code>post</code>, тому час життя посилання, що повертається, пов'язаний з часом життя аргументу <code>post</code>.</p>
<p>І ось, ми закінчили - тепер все у Блоці коду 17-11 працює! Ми реалізували патерн "Стан", який визначає правила процесу роботи з дописом у блозі. Логіка, що пов'язана з цими правилами, знаходиться в об'єктах станів, а не розпорошена по всій структурі <code>Post</code>.</p>
<blockquote>
<h4 id="Чому-не-Енум"><a class="header" href="#Чому-не-Енум">Чому не Енум?</a></h4>
<p>Можливо, вам було цікаво, чому ми не використовували <code>enum</code> з різними можливими станами допису як варіантів. Це, безумовно, одне з можливих рішень, спробуйте його реалізувати та порівняйте кінцеві результати, щоб обрати, який з варіантів вам подобається більше! Одним з недоліків використання перерахунку є те, що в кожному місці, де перевіряється його значення, потрібен вираз <code>match</code> або щось подібне для обробки всіх можливих варіантів. Можливо в цьому випадку нам доведеться повторювати більше коду, ніж це було в рішенні з трейт-об'єктом.</p>
</blockquote>
<h3 id="Компроміси-Шаблону-Стан"><a class="header" href="#Компроміси-Шаблону-Стан">Компроміси Шаблону "Стан"</a></h3>
<p>Ми показали, що Rust здатен реалізувати об'єктоорієнтований шаблон "Стан" для інкапсуляції різної поведінки публікації в кожному стані. Методи в <code>Post</code> нічого не знають про різні види поведінки. З таким способом організації коду, нам достатньо поглянути тільки на один його фрагмент, щоб дізнатися відмінності в поведінці опублікованого допису: в реалізацію трейту <code>State</code> у структури <code>Published</code>.</p>
<p>Якби ми збиралися створити альтернативну реалізацію, не використовуючи патерн "Стан", ми могли б використовувати вирази <code>match</code> в методах структури <code>Post</code> або навіть в коді <code>main</code> для перевірки стану допису та зміни його поведінки в цих місцях. Це означало б, що нам би довелося аналізувати декілька фрагментів коду, щоб зрозуміти як себе веде допис в опублікованому стані! Якби ми вирішили додати ще станів, стало б ще гірше: кожному з цих виразів <code>match</code> знадобилися б додаткові гілки.</p>
<p>За допомогою патерну "Стан" методи <code>Post</code> та ділянки, де ми використовуємо <code>Post</code>, не потребують використання виразів <code>match</code>, а для додавання нового стану потрібно буде тільки додати нову структуру та реалізувати методи трейту для цієї структури.</p>
<p>Реалізацію з використанням патерну "Стан" легко розширити для додавання нової функціональності. Щоб побачити, як легко підтримувати код, який використовує даний патерн, спробуйте виконати декілька з пропозицій нижче:</p>
<ul>
<li>Додайте метод <code>reject</code>, який змінює стан публікації з <code>PendingReview</code> назад на <code>Draft</code>.</li>
<li>Вимагайте два виклики метода <code>approve</code>, спершу ніж переводити стан в <code>Published</code>.</li>
<li>Дозвольте користувачам додавати текстовий вміст тільки тоді, коли публікація знаходиться в стані <code>Draft</code>. Порада: нехай об'єкт стану вирішує, чи можна змінювати вміст, але не відповідає за зміну <code>Post</code>.</li>
</ul>
<p>Одним з недоліків патерну "Стан" є те, що оскільки стани самі реалізують переходи між собою, деякі з них виходять пов'язаними один з одним. Якщо ми додамо інший стан між <code>PendingReview</code> та <code>Published</code>, наприклад <code>Scheduled</code> ("заплановано"), то доведеться змінювати код в <code>PendingReview</code>, щоб воно тепер переходило в стан <code>Scheduled</code>. Якби не потрібно було змінювати <code>PendingReview</code> при додаванні нового стану, було б менше роботи, але це означало б, що ми переходимо на інший шаблон проєктування.</p>
<p>Іншим недоліком є дублювання деякої логіки. Щоб усунути деяке дублювання, ми могли б спробувати зробити реалізацію за замовчуванням для методів <code>request_review</code> та <code>approve</code> трейту <code>State</code>, які повертають <code>self</code>; однак, це б порушило безпечність об'єкта, тому що трейт не знає, яким конкретно буде <code>self</code>. Ми хочемо мати можливість використовувати <code>State</code> як трейт-об'єкт, тому нам потрібно, щоб його методи були об'єктно-безпечними.</p>
<p>Інше дублювання містять подібні реалізації методів <code>request_review</code> та <code>approve</code> у <code>Post</code>. Обидва методи делегують реалізації одного й того самого методу значенню поля <code>state</code> типа <code>Option</code> і встановлює результатом нове значення поля <code>state</code>. Якби у <code>Post</code> було багато методів, що дотримувалися цього шаблону, ми могли б розглянути визначення макроса для усунення повторів (дивись секцію <a href="ch19-06-macros.html#macros">"Макроси"</a><!-- ignore --> розділу 19).</p>
<p>Реалізуючи патерн "Стан" таким чином, як він визначений для об'єктноорієнтованих мов, ми не використовуємо переваги Rust на повну. Нумо подивімось на деякі зміни, які ми можемо зробити в крейті <code>blog</code>, щоб неприпустимі стани й переходи перетворити в помилки часу компіляції.</p>
<h4 id="Кодування-Станів-та-Поведінки-у-Вигляді-Типів"><a class="header" href="#Кодування-Станів-та-Поведінки-у-Вигляді-Типів">Кодування Станів та Поведінки у Вигляді Типів</a></h4>
<p>Ми покажемо вам, як переосмислити патерн "Стан", щоб отримати інший набір компромісів. Замість того, щоб повністю інкапсулювати стани й переходи, таким чином, щоб зовнішній код не знав про них, ми будемо кодувати стани з допомогою різних типів. Отже, система перевірки типів Rust буде перешкоджати спробам використовувати чернетки там, де дозволені тільки опубліковані дописи, викликаючи помилки компіляції.</p>
<p>Розгляньмо першу частину <code>main</code> в Блоці коду 17-11:</p>
<p><span class="filename">Файл: src/main.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use blog::Post;
</span><span class="boring">
</span>fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
<span class="boring">
</span><span class="boring"> post.request_review();
</span><span class="boring"> assert_eq!("", post.content());
</span><span class="boring">
</span><span class="boring"> post.approve();
</span><span class="boring"> assert_eq!("I ate a salad for lunch today", post.content());
</span>}
</code></pre>
<p>Ми все ще дозволяємо створювати нові дописи у чернетці використовуючи <code>Post::new</code> і можливість додавати текст до змісту повідомлення. Але замість метода <code>content</code> у чернетці, що повертає порожню стрічку, ми зробимо так, що у чернеток взагалі не буває методу <code>content</code>. Таким чином, якщо ми спробуємо отримати вміст чернетки, отримаємо помилку компілятора, що повідомляє про відсутність методу. Як результат ми не зможемо випадково відобразити вміст чернетки допису в програмі, що працює, тому що цей код навіть не скомпілюється. В Блоці коду 17-19 показано визначення структур <code>Post</code> та <code>DraftPost</code>, а також методів для кожної з них:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground">pub struct Post {
content: String,
}
pub struct DraftPost {
content: String,
}
impl Post {
pub fn new() -> DraftPost {
DraftPost {
content: String::new(),
}
}
pub fn content(&self) -> &str {
&self.content
}
}
impl DraftPost {
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
}
</code></pre>
<p><span class="caption">Блок коду 17-19: Структура <code>Post</code> з методом <code>content</code> та структура <code>DraftPost</code> без методу <code>content</code></span></p>
<p>Обидві структури <code>Post</code> та <code>DraftPost</code> мають приватне поле <code>content</code>, що зберігає текст допису. Структури більше не мають поля <code>state</code>, тому що ми перемістили логіку кодування стану в типи структур. Структура <code>Post</code> буде являти собою опублікований допис, і в неї є метод <code>content</code>, який повертає <code>content</code>.</p>
<p>У нас все ще є функція <code>Post::new</code>, але замість повернення екземпляра <code>Post</code> вона повертає екземпляр <code>DraftPost</code>. Оскільки поле <code>content</code> є приватним і немає ніяких функцій, які повертають <code>Post</code>, вже не вийде створити екземпляр <code>Post</code>.</p>
<p>Структура <code>DraftPost</code> має метод <code>add_text</code>, тому ми можемо додавати текст до <code>content</code> як і раніше, але врахуйте, що в <code>DraftPost</code> не визначений метод <code>content</code>! Тепер програма гарантує, що всі дописи починаються як чернетки, а чернетки не мають контенту для відображення. Будь-яка спроба подолати ці обмеження призведе до помилки компілятора.</p>
<h4 id="Реалізація-Переходів-як-Трансформації-в-Інші-Типи"><a class="header" href="#Реалізація-Переходів-як-Трансформації-в-Інші-Типи">Реалізація Переходів як Трансформації в Інші Типи</a></h4>
<p>Як же нам опублікувати допис? Ми хочемо забезпечити дотримання правила, відповідно якому чернетка допису повинна бути перевірена та схвалена до того, як допис буде опублікований. Допис, що знаходиться в стані очікування перевірки, також не повинен вміти відображати вміст. Реалізуймо ці обмеження, додавши ще одну структуру, <code>PendingReviewPost</code>, визначивши метод <code>request_review</code> у <code>DraftPost</code>, що повертає <code>PendingReviewPost</code>, і визначивши метод <code>approve</code> у <code>PendingReviewPost</code>, що повертає <code>Post</code>, як показано в Блоці коду 17-20:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub struct DraftPost {
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Post {
</span><span class="boring"> pub fn new() -> DraftPost {
</span><span class="boring"> DraftPost {
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&self) -> &str {
</span><span class="boring"> &self.content
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>impl DraftPost {
// --snip--
<span class="boring"> pub fn add_text(&mut self, text: &str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn request_review(self) -> PendingReviewPost {
PendingReviewPost {
content: self.content,
}
}
}
pub struct PendingReviewPost {
content: String,
}
impl PendingReviewPost {
pub fn approve(self) -> Post {
Post {
content: self.content,
}
}
}
</code></pre>
<p><span class="caption">Блок коду 17-20: <code>PendingReviewPost</code>, що створюється шляхом виклику методу <code>request_review</code> екземпляру <code>DraftPost</code> і метод <code>approve</code>, який перетворює <code>PendingReviewPost</code> в опублікований <code>Post</code></span></p>
<p>Методи <code>request_review</code> та <code>approve</code> забирають у володіння <code>self</code>, таким чином поглинаючи екземпляри <code>DraftPost</code> і <code>PendingReviewPost</code>, які потім перетворюються в <code>PendingReviewPost</code> та опублікований <code>Post</code>, відповідно. Таким чином, в нас не буде ніяких довгоживучих екземплярів <code>DraftPost</code>, після того, як ми викликали в них <code>request_review</code> і так далі. У структурі <code>PendingReviewPost</code> не визначений метод <code>content</code>, тому спроба прочитати її вміст призводить до помилки компілятора, як і у випадку з <code>DraftPost</code>. Тому що единим способом отримати опублікований екземпляр <code>Post</code>, у якого дійсно є визначений метод <code>content</code>, є викликом метода <code>approve</code> у екземпляра <code>PendingReviewPost</code>, а единий спосіб отримати <code>PendingReviewPost</code> - це викликати метод <code>request_review</code> в екземпляра <code>DraftPost</code>, тобто ми закодували процес зміни станів допису за допомогою системи типів.</p>
<p>Але ми також повинні зробити невеличкі зміни в <code>main</code>. Методи <code>request_review</code> та <code>approve</code> повертають нові екземпляри, а не змінюють структуру, до якої вони звертаються, тому нам потрібно додати більше виразів <code>let post =</code>, затіняючи присвоювання для збереження екземплярів, що повертаються. Ми також не можемо використовувати твердження (assertions) для чернетки та допису, який очікує на перевірку, що вміст повинен бути пустим рядком, бо вони нам більше не потрібні: тепер ми не зможемо скомпілювати код, який намагається використовувати вміст дописів, що знаходяться в цих станах. Оновлений код в <code>main</code> показано в Блоці коду 17-21:</p>
<p><span class="filename">Файл: src/main.rs</span></p>
<pre><code class="language-rust ignore">use blog::Post;
fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
let post = post.request_review();
let post = post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
</code></pre>
<p><span class="caption">Блок коду 17-21: Зміни в <code>main</code>, які використовують нову реалізацію процесу підготовки допису блогу</span></p>
<p>Зміни, які нам треба було зробити в <code>main</code>, щоб перевизначити <code>post</code> означають, що ця реалізація тепер не зовсім відповідає об'єктноорієнтованому патерну "Стан": перетворення між станами більше не інкапсульовані всередині реалізації <code>Post</code> повністю. Проте, ми отримали велику вигоду в тому, що неприпустимі стани тепер неможливі завдяки системі типів та їх перевірці, що відбувається під час компіляції! Це гарантує, що деякі помилки, такі як відображення вмісту неопублікованого допису, будуть знайдені ще до того, як вони дійдуть до користувачів.</p>
<p>Спробуйте виконати завдання, які були запропоновані на початку цього розділу, у версії крейта <code>blog</code>, яким він став після Блока коду 17-20, щоб сформувати свою думку про дизайн цієї версії коду. Зверніть увагу, що деякі інші завдання в цьому варіанті вже можуть бути виконані.</p>
<p>Ми побачили, що хоча Rust і здатен реалізувати об'єктноорієнтовані шаблони проєктування, в ньому також доступні й інші шаблони, такі як кодування стану за допомогою системи типів. Ці шаблони мають різні компроміси. Хоча ви, можливо, дуже добре знайомі з об'єктноорієнтованими патернами, переосмислення проблем для використання переваг і можливостей Rust може дати такі вигоди, як запобігання деяких помилок під час компіляції. Об'єктноорієнтовані патерни не завжди будуть найкращим рішенням в Rust через наявність деяких можливостей, таких як володіння, якого немає в об'єктноорієнтованих мов.</p>
<h2 id="Підсумок"><a class="header" href="#Підсумок">Підсумок</a></h2>
<p>Незалежно від того, вважаєте ви Rust об'єктноорієнтованою мовою чи ні, прочитавши цей розділ, ви тепер знаєте, що можна використовувати трейт-об'єкти для впровадження деяких об'єктноорієнтованих можливостей у Rust. Динамічна диспетчеризація може дату коду певну гнучкість в обмін на невеличке погіршення швидкодії програми під час виконання. Ви можете використовувати цю гнучкість для реалізації об'єктноорієнтованих патернів, які можуть покращити супроводжуваність вашого коду. Rust також має особливості, такі як власність, які не мають об'єктноорієнтовані мови. Об'єктноорієнтовані патерни не завжди будуть найкращим способом скористатися перевагами Rust, але є доступною опцією.</p>
<p>Далі ми розглянемо патерни, які є ще однією особливістю мови Rust, що дає більше гнучкості. Ми трохи зустрічалися з ними впродовж всієї книги, але все ще не проаналізували всі їх можливості. Вперед до нових можливостей!</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch17-02-trait-objects.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch18-00-patterns.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="ch17-02-trait-objects.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch18-00-patterns.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>