-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy paththe-learn-d-developer-design-patterns.rtf
executable file
·3615 lines (3614 loc) · 792 KB
/
the-learn-d-developer-design-patterns.rtf
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
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 Courier;}}
{\colortbl;\red255\green0\blue0;\red0\green0\blue255;}
\widowctrl\hyphauto
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 Preface\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I decided to write this book because I thought it would be interesting to cover Design Patterns using a test-driven approach\u8212-I\u8217'm a sucker for \u8220"killing two birds with one stone\u8221". Because of this choice, the chapter on Observer pattern serves dual purposes with a particular focus on getting the reader up to speed on TDD basics. More advanced TDD topics like proper use of {\i test doubles}, writing testable code, etc., will not be addressed (although I try to provide links for further investigation where appropriate). All this said, the primary subject of this book is not TDD, it\u8217's Design Patterns! Also, please be forwarned that we will not be able to be particularly rigorous in our test coverage and will likely be omitting a lot of defensive code that would be required of a \u8220"real system\u8221". I have chosen understandibility over robustness for an obvious reason\u8212-I don\u8217't want the reader to get lost in details that don\u8217't really pertain to the point being made. This is a fairly typical approach, but I apologize in advanced if I somehow insult your sensibilities!\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Programming Languages}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Most of the examples will be in Java, PHP, and JavaScript, but some might use other languages. This should be fine since:\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab TDD and Design Patterns should be language agnostic\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab The examples will be simple enough to follow (even if in an unfamiliar language)\sa180\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Resources}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I\u8217've generally listed any resources as clickable web links that you can learn more from, or, as links to where you might purchase the book that I\u8217'm referencing.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Line breaks in code}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I\u8217've taken the liberty of purposely wrapping long lines that won\u8217't fit within the width of the page.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Also, I\u8217've found that the generated code samples for PHP do not show up correctly unless I start and end the sample code blocks out with corresponding PHP opening and closing tags. So that\u8217's why every darn PHP code snippet has these!}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Contributions}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I am definitely open to collaborative authorship (hey, I did put it on github!), provided that other authors follow the general style and spirit of the book. At some point, I\u8217'll try to define this all in a more concrete way. If you do want to contribute, you\u8217'll probably want to have a good look at the commented Makefile, and also notice the use of \u8220"extra lines\u8221" between code samples. I\u8217've managed to find workarounds for the somewhat finicky pandoc/docbook tool-chain (and I\u8217'm thankful that it works at all since these tools really make life so much easier!)\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Special acknowledgement}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I feel obliged to commend Addy Osmani on his countless community contributions, particularly in developer education, and acknowledge that seeing the impact of his work inspired me to write myself. Also, the book: {\field{\*\fldinst{HYPERLINK "https://github.com/addyosmani"}}{\fldrslt{\ul
Essential JS Design Patterns
}}}
is where I shamelessly lifted the pandoc build process being used here!\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 Introduction\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Why should we study design patterns?}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 As design patterns help us to use object-oriented best practices, it is useful to already have some meaningful experience in object oriented programming before attempting a deep study of design patterns. In fact, we have seen some try to dissuade a study of design patterns until the reader has reached an architect or designer skill level (whatever exactly that means). Obviously, the more prerequisite knowledge you have, the faster and you\u8217'll be able to grasp difficult abstract concepts; so perhaps there\u8217's some truth to the level of readiness that\u8217's ideal.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 However, we would assert that you need not yet be an object oriented master to benefit from an initial study of design patterns. In fact, object oriented and good design go hand in hand\u8212-therefore, studying good design should reinforce your understanding of some object oriented concepts. Examples of design pattern implementations show object-oriented best practices \u8220"in action\u8221", and, since many of us learn best by example, the study of the two at the same time just might be complimentary.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Before reading this book, please take some of the following disclaimers and suggestions in to account:\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab design patterns are an ongoing study; you\u8217'll likely learn new things when you return to the material at a later date\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab therefore, assume that you\u8217'll need to return to the materials at later stages of your career\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab if we don\u8217't explain something in a way that gets through to you, don\u8217't give up; try to look at a few similar examples on the web, or in other design pattern books, and look for the similarities\u8212-it\u8217'll start to make sense!\par}
{\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Our goal is simply to \u8220"get your feet wet\u8221" with design patterns and not to cover them exhaustively.\sa180\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 TDD\par}
{\pard \ql \f0 \sa180 \li0 \fi0 As we have stated, TDD is {\i not} the focus of this book, just an approach we\u8217've used to examine design patterns. However, unlike the majority of the book, the next chapter on Observer pattern {\b does} try to act as a sort of \u8220"TDD: up and running\u8221" tutorial\u8212-we painstakingly examine a TDD session (as we implement the Observer pattern) and go through each test case one by one in order to quickly bring you up to speed in TDD.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i If you already have a lot of experience doing test-driven development, you may be \u8220"put off\u8221" by the verbosity of that chapter. Feel free to jump ahead, and, rest assured that subsequent chapters will be much more \u8220"to the point\u8221".}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Design Patterns\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We would be remiss not to mention the pioneers known as the {\i Gang of Four} who, with their book {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Design_Patterns"}}{\fldrslt{\ul
Design Patterns: Elements of Reusable Object-Oriented Software
}}}
, described several reusable patterns that can be used to solve common recurring software design dilemnas.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We will discuss several, but not all, of the {\field{\*\fldinst{HYPERLINK "http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=pd_bxgy_b_text_y"}}{\fldrslt{\ul
GoF
}}}
patterns, since they lay the framework from which new design patterns have evolved. As every pattern has its pros and cons, we will list the ones we\u8217've noticed as applicable. As we cover each particular pattern, we will not be adhering to any rigid {\i pattern structures} like those presented in the following section. Our goal is to distill the material in an accessible way. Hence, we advise you to view this book as a supplementary material on the subject.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Pattern Structures\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Formal design pattern descriptions are known to adhere to certain well-defined {\i pattern structures}. {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Christopher_Alexander"}}{\fldrslt{\ul
Christopher Alexandar
}}}
is an architecture academic who, while pioneering the whole design pattern concept in the first place, provided the {\field{\*\fldinst{HYPERLINK "http://c2.com/cgi/wiki?AlexandrianForm"}}{\fldrslt{\ul
Alexandrian Form
}}}
. The {\i Gang of Four} uses the {\field{\*\fldinst{HYPERLINK "http://c2.com/ppr/about/portland.html"}}{\fldrslt{\ul
Portland Form
}}}
(which includes no less than: {\i intent, motivation, applicability, structure, implementation, sample code, known uses, and related patterns sections}).\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 Observer Pattern\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Tip:} {\i This is a long chapter that goes through the TDD process with a fine tooth comb. If you\u8217're already comfortable with TDD, feel free to {\field{\*\fldinst{HYPERLINK "#final-implementation"}}{\fldrslt{\ul
skip to the bottom
}}}
to see the implementation.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Overview\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Wikipedia describes the {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Observer_pattern"}}{\fldrslt{\ul
Observer Pattern
}}}
as follows:\par}
{\pard \ql \f0 \sa180 \li720 \fi0 The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods\u8230?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 There are many event based systems that are quite {\field{\*\fldinst{HYPERLINK "https://github.com/millermedeiros/js-signals/wiki/Comparison-between-different-Observer-Pattern-implementations"}}{\fldrslt{\ul
similar
}}}
in spirit to the Observer pattern such as: {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern"}}{\fldrslt{\ul
Pub/Sub
}}}
, {\field{\*\fldinst{HYPERLINK "https://github.com/joyent/node/blob/master/lib/events.js"}}{\fldrslt{\ul
EventEmitter
}}}
({\field{\*\fldinst{HYPERLINK "https://github.com/joyent/node"}}{\fldrslt{\ul
node.js
}}}
), {\field{\*\fldinst{HYPERLINK "https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html"}}{\fldrslt{\ul
NSNotification
}}}
({\field{\*\fldinst{HYPERLINK "https://developer.apple.com/technologies/mac/cocoa.html"}}{\fldrslt{\ul
Cocoa
}}}
), etc. These all have in common the ability to dynamically notify one to many interested parties via some sort of broadcast mechanism. In the case of the Observer, a {\i subject} calls the update method of one to many {\i boserver} instances (it does this without needing to know anything specific about these parties other than that they implement a common interface to recieve said notifications). In object oriented circles, this decoupled use of composition and interfaces is called {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Design_Patterns#Introduction.2C_Chapter_1"}}{\fldrslt{\ul
{\i programming to an interface not an implementation}
}}}
. This is a key object oriented principle that allows us to maintain orthogonality in our systems.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Another feature of this pattern is that observers can be added or removed at runtime\u8212-thus providing a means to \u8220"hot swap\u8221" components easily when needed.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 At its core the pattern involves the following steps:\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab Allow {\i observers} to register (and also unregister) to recieve notifications.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab Keep these observer instances in a suitable data structure such as a {\i List} or {\i Array}.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab When state changes (e.g.\u160?a new article is published, a user logs in, a track is played, etc.) these observers are notified by the {\i subject} who calls an interface defined update method on each of the observers.\sa180\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Observer Pattern Class Diagram\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The following is a class diagram that represents the Observer Pattern we\u8217'll be discussing in this chapter:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\pict\pngblip 89504e470d0a1a0a0000000d49484452000002cf0000015b08060000002a6d637f00006a8a4944415478daec9d079814c5d6861bd8abe4bce4285104c93948ce082208888020228848109120281914c949822241e5929392240ae27f5514098a041550405141c4849e7fbeeaadd99ed9ee9e990db0b3fbf13cefc37698eaea9e99ea774e9faa327efef9678310420821841012185e0442082184104228cf841042082184509e092184104208a13c134208218410427926841042082184f24c082184104208e599104208218410427926841042082184f24c082184104208e599104208218410ca33218410420821946742082184104228cf841042082184509e09218410420821946742082184104228cf841042082184509e092184104208a13c134208218410427926841042082184f24c082184104208a13c134208218410427926841042082184f24c082184104248729667cf3f21848435911ed2b2412584104279be45f27c488410128644c9732908341b544208219467ca332124803c474444d4f5fc5f880d2a218410ca33e59984c081bfff9683fffcc36b91cce43975ead494674208219467ca73f264f7f5eb32febfff95f6fdfac9d353a6c8d24f3e09fab5058a1797a1afbeeab87dfabbefca6b1f7ec8eb4c792684104228cf94e7f0e7dd4b97244b8e1c727fcf9e4a9c6bb668218366cc08eab5ef5dbd2a2952a490251f7de4b84fb5a64d65f4f2e571aae3c19b37e58d8f3f5692cff78cf24c082184509ec96da371a74ed2b677ef58bd76de9e3df29f3bee90fd7ffe694a6e08e91b10e260b7419c21e99075be67946742082184f24c6e1ba52a5756d166e42efb6feb3d7ebc347de411eff29ad3a725326f5e15adc63222d5252a5490610b1648debbee923b52a796767dfb7af77f75ff7ec953b8b0b7ec7dbfff2e9d070f96ec79f248eab469a5fbf3cf7bf7ddffc71ff2c4d8b192ab6041b9334d1ac99835abbcfebfffc9bcbd7bd5df90671cbbfd534ff17da33c134208219467727b80f8424c2bd6ab271bcf9df3d956b57163e9366c987779fcca952ac5c31ab5ce9e3bb7f49d3c598975d7a14395582dffec331fb9d6fb637b93871f961d3ffda472a153a64c29db7efc516d7b7cf46825da93d6ae5591e74de7cf2ba18678b778f45169f0d0432a6d4347b909e59990447983e518f084e3f5539e29cf491f74164c9731a38aecbe79f4a8777da66cd964e2ead5dee5479e7d56aa376be6d35970f0ecd9de65bc16efedcceddbd572a38e1da5f5e38fabbf377ff79da4cd9041a66fdd2a4b0f1f9619dbb6a97d21ddef5cbc2869d2a757f5b0ab5fc98a159588f3bda23c13120ef2cc768270bc7eca331b9764c0ca1327943c576bd2442daf3b7b567db9d67ffdb5779f4af5eb4b8f91231d3b0bbef67fff272953a592f5df7ca396f3172b26cfcd9faffeeef2dc732a6a5dfebefbbc54a85b57f6deb8a122d239f3e7970ffefd3746bd1069465ef5dcddbbf93e519e09a13c13c2f1fa29cf24f1d0b27b772955a58afa1b11e70c99337bb76dbd7c59d267ca242fad5f6fdb591020df59e7486bb946de32969b75e922a5ab55b33d2e5e933932d2761b3a0be2f3c2ce8294674228cf84b01da73c93db066474c2aa55b2ebda35b5bceae44995baf1ec9c396a79e4922592397b769567bcf0e0412956b6acfab2e9bc68a45140a6df3e7edc94e9bd7b956c23828d65448a23fef31f95b78ce581d3a7ab8e80884e63f9fdbffe92ad3ffca0feee3f75aa2a1bf541f419291e3b7efe596d9bb36b97daa6eb49d8e8124279e610abe83bc36bc1769cf24c6e2998b804235e207a5cb85429b9bb522525b8d6348e5c050ac87feebc53751c7c66e6cc189d05ebb76ba7f29ed1d10f23628c5eb1c2bbddbfb320d233d0e90fd1e8dc850aa91491716fbfed9da405237ee073817272e4cba7d24674c4bb60c9922a328d0e8b7cefd8e8124279befdbcb279b37a32782b8f89279db81fe52d5244dd2774b086b01da73c935b06c66546c479cbf7df3b8eb7ac23c076916b34642a527ce1428c7ce5ca0d1baa513862cc66f8ebafaa83a05d99dbaf5c919dbffc62bb0dc7e034e06c7409a13c270ed0217cc0b46941ef8fa796d63e34b1019dd1319c29db61b6e394679264c0b4de88503ff5d24b2ae583a9166c7409a13c277e16bcff7ebcee6747ad56ada4d3a04171aa67cf175e50c3aa26f4f540441df312c4d77e6cc729cf9467e208f29f3b0e18a052369ca2d984f24c08e5397180486e991a35d4484aa1ee874ee0cfcc9ae5bb3c73a639d95581026a7fa407ea09b7d2a44b2719b26451a97e53366ef4a6e5d56ddb56ad477a1efab858cb432a2166c0c5f6a75f7945a5f521dd10650c5fb850ed876396ab5d5b056cf0c4531f13601e81563d7aa834410c955abc7c799f4ef04ec7469918010a9175a41c3a5d9760f7633b4e79a63c13427966a34b4818dfdf30ce3ee416232061c2aad8ec87a1461109b62e63d2acf6fdfaa931ff21a6751f78c09bae875ce5b67dfa2869d51dc921bbf7f7eca9d20087cc9d2b45ca94f1290f7d5dfa4e9aa4d20b7779cae83470a094ad554b95a16575d29a35b2ecd34f55994d3a7796861d3af8a40f223f7aea962d2acdf0ad63c77cb6391d5bcfa87bff638f293946a77627390e763fb6e39467ca3321946736ba8484d9fd0d935495a95e5d0931c4382efbd9c9333a92ebe5e18b1629a1744adb8074e25a4d5eb74e3db5440a04e609d04f2d515e9b5ebd7c8ed9fdf9e75da3e443e6cd531dd8ade5dbe565073ab695b567ce28c95672fcca2bb2e7b7df6c8f1dec7e6cc729cf94674228cf6c54090983fbdb8acf3f57232021a502438bc6753f3b79b62ebfb86c991a11c3499ef530a490f47b6bd6f4f2eafefdb6e539c9f3cb1b36c83d55abaa3a038ce6642d1f7d71fceb1ee8d8fe6c3a7f5eee6bd346cdba8b1f054ed724d8fdd88e539e29cf84509e092161727f43be71c98a15959cbacddc1a68bfb8caf39a53a7545b126c64db4e9e91ce912a2242e564abf9095e7fdd2bcfba7c44a363449e031c5bb3e1db6fe5c1279f5439d34f4e9ca8d24fe2b21fdb71ca33e59910ca331b5d42c2f4fe3665d32635be7ff93a7564f67bef85bc5fa8f28c7c65ff91322a3768a0d6e921ec30e6bf1ea52918795e78e0809267a44c20e502b3d5a203a0de8eb291cbbce4a38fd4f27fbffcd23be4a9dbb121c3e8a80819c690ab6ed21ccc7e6cc729cf94674228cf6c74094922f73774a6abdeac59c8fb852acf4807c95fac984a07d19162086f8de6cd55be313af665cd99d39b2a126cda063a25de913ab564cf93478dc0819409dd6910f303a01c3d0117f291756745b7633f366a941a721542ed764d82dd8fed38e599f24c08e5998d2e21bcbfc50abb09afdc26cf0a060c49a7cb84c8facb2c26e0729af02baec7663b4e79bea58d0b21247c89888860a34b08e599509e29cf6c5c0821c134ba946742787f239467ca331b174208e59910dedf08e599509e092189bfd1f5141de121251b6942792684f24c798e87c6e5c0df7fc7e8644008493a8d6e9b366d765fb972e54536d22421f07c74ffe3a1a687111e32c6e2f505f17aca33613b4e790e1b79b61bb626b9f1d2faf5b2f8d021db6daf6cdeaca61865a34028cf84c410df3459b366bd52bd7af54ff0f9f5fccb19c26b5378b8d3439d3367ce8c8b5a979af24cd88e539e29cf6140a92a55a4f3e0c1b6db1a75ec2803a64d0baa9c8de7ce7907862784f24c92813c438053a3198d853c172b57aedcd13ffffc73a4e7ef210d1b363ce8f9bf1be599b01da73c539ec35c9e43c17f9a54426e45a3eb7969b6c993272fcd952bd7450fdf4f9d3a7589675d16ab3c5fba7469f48e1d3ba6619f92254b7ee5d9dedaf2fa1a9d3a75da9e254b969f0a152af48d673987655be4c08103d7605be9d2a54f7896ebe96d38a667b9fff2e5cbe761fb82050bb03cc85ab7d5ab57cff6aceb1d6a59b179fc4f6eab44872ccf9a0b172e8cf1bcee39eb6796f2ccd44bca33e5f9b6ca3306396fd6a58b9ae5275bae5cd2a25b37d9f1d34f3ef28cd97c3a0e18a0f6c957b4a84a63b04ed359a5512335352766307af7d225efb6ad972f4bddb66dd5b682254bca9c5dbbbcdb70cc81d3a7ab2936b11dcbfd5f79c5a76eedfaf69521f3e6855cd67b57afbad6cb9f27c68e95f64f3d152b79c6b19f99352be0f5e83d7ebca449974ead2f50bcb84cd9b8910d09b9258d6ee7ce9db7b56fdf7ea7a788bb41bb76eddeebd6addb3b5679ce9f3fff39cfb6811ecacc99336771b66cd97ef4fc9d0adb1b376e7c00e21a15452ce4e10efdda0e1d3aec58e8f90799f5d0a762c58a47ace5e6c891e37294f814f3d0a368d1a267504e945065f79ccf0de4b4c6a22c76704c06f28c48f3810307a678fe6f3a66cc9815c9599eed52043ff8f75f99b476ad741b364cdda3c6bcf9a6bcffd75f0c80519e29cf092dcf551b375673cdbf7dfcb8025370fa4f058aa93efb4f9d2a2b8e1c91563d7aa869380fdebca9b697a95143892bbec4ebce9e95fd7ffee97d6de5860de5fe9e3d95cc0e993b57cd756f2d377364a4f49d3449569d3c2923162f963c850bab72942cfff083dc99268d37cd2194b2f02bdbad5efe4078f5b4a5a1cab37fc3e4745ccca6846bddb64f1ff543404f554a484236ba9e97158e9296569675ada3d615d0627af4e8d189d64875d4f6ea586ed5aad5de2e5dba6cd5926bd9efaea8fdda4489798554a952ddf4fc9f4b97bb64c992572dfba74f9b36ed75cffff7452d0ff294fb6e6cca22c9469eeff0902eeaef641d79f64f1144900bf74504637a8d19234f4e9c28c5cb9797bb2b555229829467ca33e53981e479ed9933ea8db446411155c6baf5df7ce3fde2751d3ad427528ded0b0f1e54cbe5ebd4916a4d9ac4c8e55d73fab4da6ff2ba754acaf18b1973d9635e7b5d6e9b5ebd7ca6ea446476de9e3d6a1951e86a4d9bc6aa2cb77ac577da867fc3e4765ca66d90db20cff5a2a425b7655ddea875759c729e73e6cc79c9b3fd693dda01d236222222fe3e7cf8f064dd694b978d5cd4a64d9bbeafc147dda9dc975f7ef90da48de0efa8d48cb6b12d8b248fb40d0e55670f9ecce62e544876fef28b77ddbedf7f57f27c5f9b369467ca33e539a11a17a43e60dde6efbef3aedb74febc5a376fef5ec72f5e961c3964d08c19ea6f4822d21452454448972143d497d75a7699ead5e5de9a35bdbcba7fbf63b9cdbb76556923f81ba91993d6ac8975594ef54a6879763b2ee599dc06792e19252d0d2ceb9a44ad2b6a27a648a788dafea05f5975a25227fa462d1789daaf71b01d1121c951d1e7e6c8bfc618d3b12d8b509e93933c5b530477fcfcb3bac7b4efd72fc67e78028b6bb1f28b2fc23af592ed3849b4f2bcf2c409f546cedab9d3bb6efad6ad6addeaafbeb29543a45360fbc4d5ab7dca826c237562f0ecd966b4f8d429b5df8c6ddb82ee88882f13a2cf53b76c51f9d7e8e810dbb29cea95d0f2ec765cca33b90df29cb256ad5a1f8d1b376eb9e7ef0c1e320f1f3e7ce57df7ddf77f3af718628a7c636c439eb387a15139cf19a2caa81d35914a8afaf5eba32afd74f9c8a56edbb6ed2e9dd28147ecfa750ef29ca270e1c25f23b27de2c48909d66da19645c25f9ed11d049f37a765cab3fdbd0643a7aaa7c69b36c5d86fe9e1c36a9b0e3e856bea25db719268e5191fd092152b4a93ce9d65d7b56bead76c83871e9252952b7bbf00f850e3438f6df8b221af0a5f3cec8fedf3f7ed53928bfdefa95a559e993933fa8bd7a08154ac57cffbe5d97dfdbaf77576d2893272152ca822dbe80061dd166a596ef5f20772fed6b163f122cf6ec7ed3470a03a078a20b9c5a36d14832c67ca94e997f4e9d3ffdaa041830f74d4598be9c89123df2a5dbaf417e83818155df646aaefbefbee9318e1029dfd1e7ffcf14d18bbd75276aeeeddbb6f417e72f1e2c54f61b40eb774103d7a428a1429fe459eb35f3d432e8b247a694e9b3973e69f3364c8700d9fdf8c19335ec5fbaab7231da84c9932c79d9629cff6f71a48b335baec3fba86e7fb25cfcd9f1fd6a9976cc749a2ee30885f7990e5f49932499af4e9a574b56adea8b3fe50a3a342c11225d4af57fc3ab446aaf3172ba61eb3e01767ad962d65ef8d1bde6df862d468de5c7d49f21629a21e19b9a583801e2347aa2f3ebe6cd6f5a196e5562f7ff0e80b8f93dce419d70ef5d2206dc4eed86ec75df1f9e76a3b1e8db975502424211a5d74044474d9667dc6a88e59184d238f8e48fbed934147811dca4e1f5f8fe4e3b32cc2e9b993a23c23d883f31db66081ed7c02d8a6ef31e19a7ac9769c84c538cff8358ae8b2ff7a3c62c1631c4452375fb8e08d485b4104584781edc02fd2772e5e8c970f5e286505aa574211e8b8b88e1c7793b0d12584f21c1b79c67074d9f3e49106eddbc7d80fa9820884e19e9e14522fd98e13362e841036ba84f0fe16e709cb5e58ba5452a74dabfa29e975c8858600f79930c1e775e1987ac9769c509e09216c740909f17ec5fb9b7b74f6c565cb2463d6ac2a4d10a368a4cb9851a563589f10876bea25db7142792684b0d1252416f7ac60253ab9dedf20ca488b405f25bb74c0704ebd643b4e28cf841036ba84249044f3fe46d88e539e29cf841036ba840499cac1fb1b613b4e79a63c1342d8e8121264149af737c2769cf24c792684b0d125244889e6fd8db01da73c539e09216c74090952a2797f236cc729cf946742884f834b0871876d05a13c539e13f4173a2124fc88888860a34b0823cf84f24c7966e49910124ca34b79268439cf84f24c79a63c134228cf8470b40d427926946742081b5d426ed538cf8430fd8ef24c794e400efcfdb7ed54a684d787f24c08671824844f1029cfb7bc7119bd7cb9ac397dfa967e2877fffaab0c5fb830a87d0b142f2e435f7d35c97f51edde873dbffd262f2e5b269d070f96ae4387cacb1b362489ebf3ccac594afa29cf84241f69a63c13ca33e53949c8f3de1b37947c2dfbf4d380fb1ebc7953def8f863d97dfd7a9c8eb9e3a79fa4668b16b2f6cc9980fbbe77f5aaa4489142967cf45192fe92dabd0ff3f6ee953c850b4bdd071e9041336648b761c3247da64cd2b27bf7b0bf3ecfcc9c297d264ca03c1392c4533428cf84ed38e539d1c93384d66dd9675b088ff6edf6853843d4206cc1d4c56efbfb7ffd257757aa24b376ee0cea98f3f6ec91ffdc7187ecfff34fd77308746ec1d42de86b138f6539f1df2fbf94741933cac8d75ff7593f73fb76f585d6b21caed767e72fbf48c6ac5965f3850b6c74094986b29dd0dfc10ffefd57dd6f9876c7f3643b4e79962173e7aaa82d1e7be7c8974f3deadff7fbefeab17ef63c792475dab4d2fdf9e77d229a9d060e94ccd9b34b961c39e4f1d1a32567fefc4a84ade5e9fd2133f51e7c50957d679a3432e6cd37bd5150c80ee439326f5e69ffd4536abddbb1edea8af21a3cf490779ffd7ffc213d5f78c17bbca2f7deeb13457d7aca142951a1820c5bb040f2de7597dc913ab5b4ebdb37607d43ad5bebc71f97ca0d1bfa5c6ba44b20ea1b9bf3f47fdfe6eede2df98a16756c3cfcdf87f275eaf85c27ab04a7cd9041fa4e9e1cf6d7a7d0dd77cb948d1b29cf84509ee39d89ab574b862c591254cef1042daee554a85b57dd039db62f3e7448a66fdd9a680478da3befc8434f3f2d7d274d52f7b5c47e9ea3de784376fcfc33dbf1e42ecf8d3b755212db6bcc185977f6ac9216e4c23679f861950e31fddd772565ca94b2edc71fd5fe109ab2b56ac98a23475474f281279e9094a95279532f505e9b5ebdbce5dfd7a68d3c366a946a18101ddc75ed9af74bd3e2d14795d0e1b53ad2e9766cbbbade53b5aa6ad4f4f1702c88e2d2c38765eb0f3f28d1bbeb9e7b7cce377beedc4a16910f8ce3e1da2cffec33d7fa865ab7a95bb6c87feebcd31b3545741dc77dedfffe2f56e7e9ffbead3a79d2365fd97a9efa7d401938474499edf68504eb463b9caf0f5efffc6baf519e09a13cdf5679de78ee9cacfffaeb90ca479bf7c2d2a5092ecfbdc78d93563d7ac4f938b139477f1004c13d1c419be65dbb4afd76ed12fd79ae3c7142aa356d1aab3e366cc793903c232f1612e28d2c7ef79d8a44e2171b0474c6b66dea0d874821ea08d15afac9273e1db5ac72eadfd9ac60891252fbfefbe5dd4b97621cbb64c58aea4b13ccb1edea8a5f9669d2a757d1702ce31858d65170f0dcfcf9aa0c2de72863f0ecd9deed6f1e3dea23964ef50db56e5bbeff5e6dc7170dcb1d070c90fb7bf68c5559b1c1fa3e208a0cf9b47be4b8e9fc79756c1db10de7eb83cfe6944d9b28cf84509e6fab3cd76ad54a3a0d1a1452d9084ac5473d0349657c11ea39fab3faabafd47d09ed7db89de723cf3e1bab73673b9e44e4d9ae735897e79e53e918e5efbbcf0b3ea410d42e438628e1b596d1a25b37f58bd1a9bc05efbfafd23ad0316ddcdb6f47a757786416b9b5d6c7346ec7b62bbbdfcb2f4bf172e57c7eb9fbd76fc8bc7992295b36c7fa21d289c8f9fa6fbe71ad6fa8750338eef8fffe5709285e8b48786ccb0a05ff321045469a8dddbecfce99a3ce15e92ee17c7d1019c70f2744c329cf84509e6bb56ca952c1f4f2c8254ba44eebd6dee5665dbaa88ed3f8e19e35674e9506f7d2faf5deed688fb03f9e72152f5f5e3d75b5ca339ed695ab5d5bb5ad48417bedc30fcd88e7f8f192265d3ab52f7ee8ebc0c4d6cb97a56edbb66a7dc1922565ceae5ddeb220ce4807d0cb0b0f1c902a8d1aa97d731528e013ac08745e682bf174d0e9bc9e9c38513d31f69ea74bbdf0b40fd15ba45622a081ebe0768efe3c3176ac3725d32ea28bb67cc4e2c5f6d29a88cf13e28ffb0d46af623b9e0ce5d9bf73986e504a57ab66fbc623c5a24cf5eade653cb2cf5ba4886a809ccad3a27cff638f7925567716449dac9d05dd8e6d573664195f20bddca86347b9b7664d9fd7e191d0532fbde45806f2799b3ef248c0fa865a379567ec91be1e23474ac57af594a4c6a5ac50f02f0339c2a922227c522cf4fb97ab60417974f8f0b0bf3e8f8e18e1d848539e09497ef25ca47469f564542f0f9836cd27d8827b07fa4cf49f3a55a521429ed0a6e954323c9aafd6a4897a648f27749039ab3c4f5ab3468d6684c04393ce9da561870ede6153ab366e2c6dfbf451c286ed580fc1c6d335dcf3d026172953c6db0ea3fe084ce8b2cbd4a82103a74f57e971484db3b677713d2f444cadfd619ceaa5b7e11e8f343bd4e1ad63c75ccfd11ffc08704a170418f109028d144e1d3c0987f344ca06824a782aca763c19cab3ee1c665d872f2c1e7febdc533cead71f6a3c2ac7af2d447c11d145638272177df0816d7948abc0874fbf161f42bd0dbffaf05aabd0b91ddbaeaef862597f89e257287ea5a3b1c3170075c4974be7c4a20c444cdf3e7edcdb693143e6ccded401b7fa865a372d9e992323d517de3a62446ccab28228c4c283071db7fb97812f3d84138f9aac6916951b345002ab73b7c2f5fae00683a800f2c029cf84509e83952f0460acd1479483b675c3b7dfaabf2153c1a46de05e8361409d1ef523e50ce54d5eb74eb5af081e41be90be0651c3b6772e5ef4e9e0adc53d3ecfcb5f2addeaa5b7a1fc8448dbd0205a9c2d572e15f5b5fe8048ece7890ef54889643b9e0ce5d9bf739f1e4d031166fc1acc5da890fa40ebc7f3784481c720880a76e8df5f26ac5aa51e69e85fc5fee5e19116240879b2102d3ca6b10a1d1e9d409ec6af5c19f0d87675c5e37e8cc6a08f8f2f0f8e095144b9902c2d60ba0c74488068a1a1c3e3b8d12b560455df50eba61b54ec6f6d10625b96153c8e728acc3a95316bc70ef578118fb70a972aa5a4b8e78b2ffa440cc2f5fae04753fb7efd383e282194e790e4cb3f6716e96278928a275c28d39a8feb2fcfe8b08d279bb8cf00b4554ec2a5834578728ba7a39a57f7ef57c1096c43745bef0f6946da069e18225dd2da693c2ee7e52f956ef5d2dbac7d9c12429ead7335e0fea2033489fd3c11a8734a39613b9e8c2749c107d8fa4bd829611f8f5b02e5dfea2f831de884e83fdc5a30c7b63ef619fbd65b3eeb10a5b4cb7d455d20da8854e2b8f83fd4fa8652b7f8b8c6f13d1412a2b36b4e9d723cf770bb3e78748a5c440eae4f08e5d95f9ef114cb9adae5265fb86fa01c48322292f8f16e7dc2679567b4a3105b9d9280f1f3dde4196d2ecab67bcc8fa0143acee1495f8c3435cf3a0482ac9db8e3725e3122b22ef5d2db10e4486879563f18bef9461d0fa32b25f6f3c47d12efd9ecf7de633b4e790e3cc909a281e87c367cd122a9d1bcb9ca97d5c387dd2e10394daa63fb92c0e0e90767a62284f2ecff9dc130a57852857b043a9b2125cd5fbe90eb8a317b913686277ac899d5a984e87c8efe17888abefebfff49b1b265bdf28c5c5ec83366b58568a35f88352a8db910d097c35a1fa4c9619d4ec5c010adfa58789287fbaade77febe7d2a9d0e010c44b7ade33fc7f5bc62e402bbd40beb911bac3b6863b22d1decb23b473b31d5f9c3fe207d02117e3d0a148619c535d5a32a25e6f344ddf16436d4be496cc793a13ce3d731867dc360e648ddc0e394b84eab4d0861a34b4842dcdf30921352be309e3c265fea3361420cf9825c210d0d7d6310e1b5ce548b7c67ccca8adc588815ee795641469998440aa983c3172e54e2a63b0daef8fc73c95fac987ab4afa3d3906c049d501e3aa721854e479b915687111e74ff13bc16c742fa1cfa16e9e158833d2f749e773a2f7fa974ab179e40a23c5c5fc822465ad2a97e76e7e80fd2e930c28553ae3326c0c2e81628a754952a3e936e25e6f344c7438c23cd769cf24c08a13c1392a4c6794644d2693638fdd8df2d4d0d32eb963a8627af3a428960927f402994b444c839468eb08e86e43f425230e71528fd0e43cba2cf52286972dbaf5c713c9edd3986f2441bd169a7b19e13e3796280048caec2769cf24c08a13c1392ac2649b955936c249634349c2b72b311cdd579c54991843e4fa4893a0dcdc7769cf24c08a13c139264e51939b6d68932923a18ad0263e123cf98e7c9769cf24c79bee5e0515e6c1f53f1fa1036ba84f0fe46d88e13ca73bc327af9726f8fdb5b05f2a0d0e923987d310ef2d0575f4df25f74bbf7011d4731652a8687c300f518eb34295c1fe41beace3a6c7409a13c13c2769cf21c368d0b7a1543be30ed69c0ce06376faa9982e23aea871ea01dc30e05da179d1430fea71ed226a962f73ea067327a7fa3e73306a2c718db183ea865f7ee617f7d3014147a73b3d12584f24c08db71ca73bc362e763d92fd85d66dd9bfa76d28bd72fdd7419c216a10b660ea62b71d634cde5da992cf10376ec7441e156630d4e33b3a9d43a0730ba66e415f9b782ccb098c7b89619bd011c3ba1e43f7e0b3a565395caf0f26d5c11045e851cd469710ca33216cc729cf8e8d4beff1e3d500efde41cd4f9f56bd54dfbd74c99c4279ee5c1595c5ac3d77dd738f120ceb6c477a3b1e7b63ac453ceac754a278ac8ff12f318e63f7e79ff7896862d0714c158da9341f1f3d5a8da90811b696671d1e0683a5a36c4cdda9c78144141475813ca3bee82080f56ec7b6ab2bcac3e42fd64957d05b571fafe8bdf7fa44519f9e32454dc58a39ed31b73dc6f96cd7b76fc0fa865ab7d68f3fae0680b7be57489740d43736e76937066abea2451d25d6ff7d285fa78ecf75b24a30c6e9c4e439e17e7d0add7d77a29d70878d2e2194674279a63c2792c605331be1f1bb5e1ebf72a5925abd8c197e30f8fb834f3e292bbff842fd8f48ad753b24b6d79831b2eeec59252dc8856df2f0c32a1d0243c5601a4b3d132184a66cad5ab2e2c811159dc4842b18b45ca75ea0bc36bd7a79cbbfaf4d1b796cd42815f14674508f7b89fc544c0d0ea1c36b75a4d3edd87675c5ec4d7a3a4f80634114971e3eaca6fb84e8e14783f57cb3e7cead64113f34703c5cdbe59f7de65adf50eb8601fa3138bc8e9a22ba8ee3bef67fff17abf3f47fdf31bdac5dbeb2f53cf5fb8032708e4e03e14382f50c58e17c7df07af4c067a34b08e59910b6e39467c7c605626c95c7479e7d56aa376be6d3f9abcb9021dee527c68e55330159b74342bc91c5efbe5391c8e95bb72a01c55cf23836440a514788d6d24f3ef1e9a8659553ffce66983da8f6fdf77b23e156307d2a229dc11cdbaeae8b0f1d9234e9d37b676fc231b0aca3e000b32aa20c2de728c31a797ff3e8511fb174aa6fa875c36c48d8bef2c409b5dc71c00035ab516cca8a0dd6f7015164c8a79e46d5caa6f3e7d5b175c4369caf0f3e9b53366d62a34b08e59910b6e39467fbc6454714f55cefa052fdfad263e448c7ce5f88f4b6edd3c7717b97e79e5391ebf2f7dde70583d1435021e1105e6b1d5a74eba66602722a6fc1fbefabb40e744c1bf7f6dbd1e9151e99456e2dd20f8239b65dd9fd5e7ed967ba4f442bfdeb3764de3cf503c3a97e88742272befe9b6f5ceb1b6addf40f9bf1fffdaf1250bc1691f0d896150afe65208a8c341bbb7d9f9d33479d2bd25dc2f9fa20328e1f4e8886b3d12584f24c7cdb1f5e07b6e394e7281071ce9039b37779ebe5cb4a6a30e7bc5de72f803cd6e18b16396e6fd6a58b94ae56cdf68303f12e53bdbacff4a398631e23373895a745f9fec71ef34aacee2c8873b27616743bb65dd9906548955e6ed4b1a3dc5bb3a6cfeb90d6f1d44b2f3996817c5e6bceb8537d43ad9bca33f6481f7ec854ac574f496a5cca0a05ff3290239c2a2222c654b158ce55b0a03c3a7c78d85f9f47478cf0e6cdb3d12524fce499241c12d50691b8131111c1763cdce579e492252aa2889ce185070f4ab1b265d59bbbf1dc399fce5fd621ddb05d0f5fe6bf1d0c9c3e5d3dfed6b9a778d4af238278548ee81e22be88e8d66ad9529587b9e2edca435a05c65fd6af457eb6de8699a1f05aabd0b91ddbaeae4819a9d3bab54fbe73ae020554241e72853aa24399ce894519f871f1f6f1e3de4e8bf8f1a15307dcea1b6addb478668e8c54d171eb8811b129cb0a5226f07e3b6df72f033faa209c48e9b1a659546ed04009ac1e1f395caf0ff2af91ca813c70ca33216129cf911e4a414cf03d21f187559e793de246943897c2e795dfdb309667480d64111daf2032e8f4e5df59d0da790fc3b961f4042d4bfedbf5681a8830e3d178ee4285d44818faf13c26d840074144053bf4ef2f1356ad9234e9d279a380fee595ab5d5b4910f264513f2df55ae80a962ca9e4099d1c031ddbaeae78dc8fd118f4f1d1a10cc78428a25c489616305d46fd76ed946861bc63743a1bbd624550f50db56e3a6504fb23d521d86bec54969527274e748ccc3a95316bc70ef5432b6bce9c52b8542925c53d5f7c51a56b84fbf5c18fa6f6fdfaf1711f21e12bcf69a304ba108957c4fa2f2a7acaeb1237f0394dcbef6d98771844c46ec7cf3f27c88c7def5cbce8ba0f5226306246a0fc5b1dadb4039d10fd875b0be6d81a8c3432f6adb77cd6214a6997fb8aba40b411a9c471edc6bc0e54df50ea161fd7383ec1f9223abbe6d429c7730fb7eb3369cd1a35020c73e5082124a637f8cb33af0ba13cdf4220b88806a2f31972a66b346faef265f5f061b70b444e13ebd8be24e1c1d30f76342184107771a64013caf36d00291b18f6eda1a79f56a91bc8378eebb4da84b097362184dc3a79a64013ca332184f24c0821418a33e599509e09219467420809529c29d084f24c08a13c134208e599509e13af3c638ce1ed57ae508808a13c1342c2509c29d084f29c408c5ebe5cd69c3eed33a41c86a6bbeb9e7bd478c1c14c838ca1c6862f5ca83a17eab1a509a13cb3412584509e09e5396ce519e34463aa6cebc81998a4021365e8990801a63ec68c70c1968bd90c6bb668216bcf9c5193b7f4993081124528cf946742480239426ce1f523c95e9eed26b6b08ed5ecbf0ee28c99dc30f985d3eb30bd356631c474cdc1d401d329df5da9929ad110cb885263b63a44af295284f2cc0695104208e5f9b6c9f390b973558477e2ead52aa502923a78f66c9f49447abef082e4c8974f4dad5cf4de7b65c9471fa96d9061ec0f79c614c8882e5bcbc4dfcb3ffb4c4de98ce3e37f4cdf8c49528a952deb3bd3dbdab552a5512325ef63de7c534da862dd5ee8eebb399909a13c539e092184509e6faf3c37eed4493265cb260f3ef9a4acfce20bf53fa2be7afb63a34649f93a7564e9e1c36adaea7a0f3ea8241bdb90878ce9b521ba48dbc0d4ccbacc36bd7a79d33afabff28a142c51c2dcc723e3b3df7b4fc9c0d6cb97cddc66cffa9cf9f3abf558bea76a5525f3d67a66cf9d5b9e7fed358a14a13cb3412584104279be7df28cdce42e438678979f183b564a55a9e21d19234dfaf42a35436fc72c81284b8b72c98a15e5e92953629439f4d557bdcbad7af490269d3b7b97312537cad06919387ec30e1dd4df8b0f1d52c744deb4b54c44bda76cda449122946736a884905bec0fbc0e84f21c05f2949172a1d33000a2c86dfbf4517f771d3a54c9b14f9ac7bc792a52ad523a3c02fd9f3bee90b9bb77bb9689148dfe53a7fa94834833d6bd7dfcb84afdd874febc5adfefe597a578b9723efb22f503421dcc081d84509e092184f24c28cf0922cff3f6ec51f2aba3c820ef5d77a99c64fcdda86347b9b7664d9fd720a5e2a9975ef2761644b9d6ce82fe65a2b360aa8888189d056bb56c292dba75938af5ea2961d6eb21ec18d2cebaefa3234678f3a909a13cb3412584509e09e5f9b6c833d22d4a54a8e0333c1cf6d343cc21df39578102b2feebaf950c23ea8c8e8310626c9fb36b97da7fd7b56b8e65bef6e1879232654a9f7d40f7e79f57a918854b9552a36be8f518d3b94eebd63ea37f200d64d5c99394284279a63c134228cf84f27cfbe4d9dab10f2007f98ed4a9bd13922037b95cedda2a929c39325249f16bfff77fdefdd1e1af60c9926adbf8952b6dcb7c76ce1cb58fffb1d121107582805bd7bf79f4a864cf93c71bb986c0b7efd78f024528cf94674208e539683c4d678487947c0f29cf093eceb31d1865c32ddf18e32fdb8d01ed46a78103555a88ddb66ec386c9d8b7de92496bd648e7c183294f84f24c792684509e43a24d9b36bbaf5cb9f2a245a65b78a8c0f794f27c4be439be412a47fa4c991c273dc1707618d379c2aa5514274228cf84101267791e3972e45b9ee67400af0de5392ce51993a0bcbc6103a58810ca33212489e269ba6ac6e77e7195e7783caf0a1e52c7d77e84f24c08a13c134292b734376adcb8f18176eddabd17ea7e93274f5eea59df7ff9f2e5f3b264c9f293e7ef8c1e22070e1cb806cba54b973ee159ae6729a35f8b162df6454646fed0a143871d9ee52a4ef21c55f6533a1fba54a9525ffa73f5ead517a2b6bb1eb3408102df228aed218dcbf905b51fa13c134228cf8490e429cd8d21c30d1a34f8c0f377d3d8ec07e1cd9123c765ccc9866922d0e10f52bcd0f32f4aa4fb54ac58f188a5acb61eca622eb571e3c62d7ffef9e7df7692679b1ce8480b4fdc71c71d7f7afeaf8b6d6ec78c7aed5df3e7cf5f1425c7039de438d8fd08e599104279268424930e839e26aa49c3860d0f428821c671d90f82bb64c99257adf289e37bfeb5f17037522152a54a75d3f37f2e9bf27b172d5af44cb0f26c795da53469d2fc8669256271ccc290ec28391ee421adc3b907b51fa13cdf3230b45ea8a37dc42798c61c636487539d49f8c9b31376df75421288480fbce9539ead5258ba468d1a1f172a54e81bcfdf75e2ba9f4d74b81e8e0fe96edab4e9fb1accb516b5fdfefaf5eb1f42d9e0aebbee3a1b8a3c7b5e5f2057ae5cdf9f3973665cb0c7b439b7bcfdfaf55b972d5bb61f31c2aecb35086a3f92cce4f9a5f5eb638cdb0c5ed9bc59567ef145821d173312f67ce185782d1393b34c5abb560d9b87e1f2d0c9d13a998b9e9ebc6ae3c692b748113579ccca1327827e6d42d4393180737f66e64c19f5c61bb2e3e79fe354d6e475ebd4c82c94e7b8479ecd280aaf24893d16812e4481a63cdb8861ab5ab56a7d0441d5a90fb1d9cf469e8b4445811bdb94552c2222e26fe44e472d3f1a8a3c2325a342850a9f23d5c3f3778a608ee977fcfc2b57ae9c932f5fbef39ebf877a481f97fd483290e78de7cea99907adeb4a55a9a2660d5c73ea94cffaf2f7dd27c3172e0c1b794614b972c3866a66c35e63c6c89313274af1f2e5e5ee4a95d479ebfd666edf2eb90a168cd56b93aa3c637af517962e553f24aa356dea9d702736e09a751932244e9f49ca33e599c49f3c5b249a024d797612ca9675ead4f95fab56adf67afeae1fea7e76d1e1f6eddbef6cdbb6ed2ecf7e05a35e9bce43060f35a2e4b930522a264e9cb80c1dfc8291677418ecd2a5cb564497ed46c4703aa6966174688c92e1216ed21ccc7e2419c973ad56ada4d3a04131e43965aa544a0c11810c57796ed7b7afe42e54c86762184c490e99bbaf4d1bef3a1cb362bd7ab17a6d529467cc1a59b6562deff223cf3e1be3339290f26cf799a43c539e49fcca33059af21ca44437efd6addb3ba1eee7905a91ab7bf7ee5b90775cbc78f153b972e5baa8533ffaf7efbff6ce3beffc3d6fdebc17705b464a84ee3418409eabe2bc90eb9c3973e69f3553a74e5d12e898972e5d1a8d099421d46ee716ec7e24cce4f99d8b17a54eebd692316b56151d7d7cf4682955b9b2cff4db75dbb6950c59b2a829b6754a46eff1e3254dba746a3d22ac98d044cbf3c3cf3c23a92222e4b9f9f31de5b956cb96f2e2b265dee5914b96a87ae8e5665dbaa847ff8f8e18a1a6ea2e59b1a2ac3a7952bd267fb16252b85429950a6215514ce3dd71c000c99a33a7e42b5a54a590043a0f7dac81d3a74bdbdebdd5f60ddf7eabea6f372578df4993d44d03292898761cd72d75dab4ea1ae0fc90a610cc6b83a9f3c20307a44aa346aa4eb90a145079d5b1391f2cf77fe59518823f64debc90cb7aefea55d77a419c91aea197577ff595a4499f5ef6fcf69bededf889b163a5fd534f052dcffa7381d7e1d8656ad4f0a675387d26ddce2fb69f7fa76b437926c9459e29d094e7db349a477a0f396dd667d3d370474588d325f43149329667081c22a7901c481022a31001bd1de907f7f7eca9c460c8dcb952a44c19b57ef7afbfaa5cdfb67dfa28c1c08c805a9e47af58218f0e1f2ee93266f4a629f8cb7391d2a5e59959b3bccb03a64d93e2e5caf9d42b5bae5c2aed61ddd9b3726fcd9a4a2e9b3cfcb0924fc81f8e65dd1f39c7fda74e9515478e48ab1e3d2453b66c72f0e64dd7f3d0afcd1c19a9e41682bef0e041755398b269538cebb5f4f061b50dd386430831c5388411d760ef8d1bb2f8d0a1a05e1b4c9d2186903344f0710d905f1de87db13b9f118b174b9ec285bd4f0230d53a526b747a432865a183a353bd765dbba6ce6fc1fbeffb748ac4938819dbb6d9de8e21e2487d09569e519fecb973ab1f276f1e3daac4b6ee030fb87e26039d5f6c3eff4ed7261ce5f9d2a577e5a79f76242a49fbfbef03f2cf3f07c35632c3bdfec1ca33059a1092ece419c2e02f7a78ecaee561cde9d36a3b3a6dbd7dfcb8bcf1f1c74a84b67cffbd6bda06e419e282e870cd162d622dcf0f3ef9a44f1e2d22835a50506708a0757feca397b7fdf8a3aa3b4438d079e0b56d7af5f2be16655b23c4fe2364a44891c21b55effefcf352a97efd58bdd6adceea9ad5a923d59a348991c31beaf9402a11919db7678f5a46141ab9c8b129cbad5e6f1d3ba6ca4234d7ba3eef5d77c9b0050be2256d03f569dca9937779f8a24592337f7ec7b40db7f38bebe7dfeeda84933cfff9e77e69dcb8aa14299257f2e5cb21274eac4c349256b76e0579e1859e0956fee6cdafc8c71fbf11b6f54f4cf24c812684242b799ebf6f9f6af4367ff79dad3ce01135b697a95e5d457e35afeedf1f509ef137a2b02953a694d1cb97c74a9eadf9c08864576ed0c067f40e7f79f6cf1fce9223870c9a3123e079f8bf564ba09df021928e6d3a5aea2fcfa1bcd6adcef81b728af408a481402091371dccfb62576ef3ae5da545b76eea6fa41fe8e8776cca72aa173a08a2ac4de7cffbec8ff40a44bfe34b9eadf5411a0fa2f74ef2ec767e71fdfcdfea9cf5f896e7eddb674ac182b912a5a425b47c76ecd848a64d1b40798e2779a6401342928d3c23cd00026a7d6c8e3c586fe4edd429d5203a3d720f24cf00db3367cf2e77dd734f0c79c6a37faf1c8f1811aff28cd404d41d3214e83cfc5f8b21e59067dda07dfb18fb0e9e3d5be5f0224a6c27cfa1bcd6adced6d7cddbbb57a507e0f5c1bc2f7652071144f479ea962d2a1d468f80119bb29cea853416fc58c27aeb507e5837fbbdf76e8b3cbb9d5f5c3fffe12ecf90bb7af52a264b7966fde35f9e29d084906493f30cd980b4223a8cce6b905ac8ae37e7d323acc809d58fe8775fbfae725b95180f1c1863a4097f7986a0205719c7b6ca73bd071f548fdf91de81ce82e933658ab33c233f151df690338ce1e1903facebea761e761284a1d6d01170fad6adde7588a44316fb4c98e05de72fcfa1bc36509d111985e422b7f89eaa555547b960de17bbf3411918520f916d8c3d6ddd166a596ef542ce315229f432d21dd019cf9aafed9382e2115444ebe34b9eed3e936ee71797cf7f38cbf39c39cf4ad6ac19256ddad452bc780159b870b85affe38fdba44b976692336756c9952b9b74ebd6c2271f1adba64f1f28bd7bb7952c5932c8d5abefc9e5cb5ba56ddbba6ab964c982b26bd71ceffe33673e23b56b9793ecd9334bc38695e5c30f5ff36ec3b17af4682579f3464a860c69a57cf9e231e473ecd827a440815c52a346199fd7fa13a8defe60df59b39ef159465de3eb78a8ffa8518fc980011dd53e458be693f5eb5ff26e3f7060a1346a54455d331c0f79e77a9bdbf5f4bffe587ee595fe3e75ebdbb79dcc9b3724e4b2f05ebad52b1879a640b3c32021c9429e91bf8951034a57aba6a40a8fd7f198ddbabd46f3e62ad71313816054081d595cf1f9e76ae40becafa377fef2ac2394c8f5b5caf3dcddbb25326f5ef9cf9d77aa0e5f90cab8ca73a38e1da56089124aa620aab376ee0cea3c9c24086206f143673bfc00400748a4545887e0b393e7605f1ba8ceb8b68882a20c8c4e821f2271399f1e2347aaf701b9bcfe9f8150ca72abd7ac1d3b546eba8e6ca3b35def71e31c6fb5e8f8874e7ff125cf769f49b7f38bcbe73f9ce5f9b7dff6c8c0819da456adb24ab06edcd8abd62307ba4183ca72fcf8db8afaf52b49b366d57da4303232b34c9ad4574e9e5ca53ac5418a7bf6bc5fc9d7dcb943a44c9922defdd7ac99249f7eba4cfef863bf74eedc443a7468e8dd86d721df7acb96a92afffad8b1b77c8e933b7776e9d7afbd1c3dfaa612c0071ea8eb787502d53b506438be8f87f290473e756a7f39726485fa91902d5b26b979d3ec44083987b8fefbef0772f6ec3a75fed6ebe2743dfdafffe2c523a470e13caa1c6cffe187ad9226cd9df2f5d7eb432e0befa55bbdfc264909060a34e59990e431ce3346b3406730fff5e874e6df114cb3f9c285588d3480686b5c67a0d3603404443721a7a88f5552833d0fa7d9f2101d45e7b250cf31d06b83a933a29c3ad2191fe7e3462865b9d50bb9ec184164d1071f4893ce9d6fcb0366bbcf6430e7179bcf7fb8a66d3cff7c77257d7af9cc99b5ea181b374ef1ae43b414ebbef966bd57b87af56ae3dd7efaf41ab57dddbac94a22d1092f55aa94f2fdf75b629c01a2a1103debeb9cf28e719c4e9d1a7b97172d1a2ef9f3e7b4dd37987a0723cff1793c94377468579f4835b61f3cb8502dd7a9535e9a34a9e695dc60afa7fff5fff5d7dd922e5d1ad9b3679e5a4614ba69d36ab12acbad5eb19c89b032a6f2e6cd9ff24c489293e7be9327abc81ea2c5f73ff698ca8bb5463f09890d1356ad52e32cebe1e2122be1f4f94f6879c6237decf3dd779bbdebce9f37478fd9bb779ead74ead754af5e466ad6bcd7cbfefdafaaed1b36bc2c55abde23152a9450142a94dbe7759f7cb23428b95db6ec4515c9b5db37987a0723cff1793cbb9ce71c39b2c88c1983d4df9053a4474444a4922143bac8efbfef0bea7ada95dbb56b73953682bf919a81687f6ccb72aa576ce439222222d69f554279262451cb33862fc384132dbb77578fad977ef209e58f241bc2e9f39fd0f28ca1eab0cfce9db3bcebb66e9daed67df5d56a5be13a75ca8c6e6edb3623468d910a0009c3a81e587efdf5915e79d6afd3b9b97191e760ea1d9ff21c9beb84740a6c5fbd7aa24f59906da44ecc9e3d38e0f574125e4832a2cf487f41fe35c6988e6d594ef58a8d3cc7e5b34a28cf8424d9e9b9092149479e91f35ab16249959b7cedda2ecf8d72873cf45003a95cb99437a7d64eb890fb8b513bf4a3feebd777abd7a3f319e419690e481578e491a6aa239a7e1d5e831cdc8f3e5aa296bffcf2bfde89454291d960ea1d9ff21cec7542be31b621cf79e2c42755ce33f6c7f67dfbe62bc9c5fe88cca3b362a0ebe974fd5106861c44647bd8b06e41bd374e65b9d58bf24c79e6752094674208e5d922cf3a5a0c09cc9429bda44f9f46aa552bed13bdb5132e8871f3e635543e2d3a006274099dbe804e77a953df2179f26457237a402075a7c10b1736abf2502f8cfc811c63742c0c556683a9777cca73b0d709634997285150958328ae35525dac587ef5430239e02d5bd6f276d80c743d9da2c52347f6509d8191e71cec7b6357965bbd28cf8410ca33212459cbb31be8e086a869283544e7b58b17dfb12d4b479411f904d6ed57ae6c0ff958f159ef84381e46b7c0481588e0e247825d041c11601d050ee57ac68650ca0a542fca332184f24c08a13c1312cf93a9509e092194674208e59910ca332184f24c08a13c13427926ec304828cf94674228cf946742792694674279a63c1327b9e275a03c2717794627bcbffe7affb61d1f43bae98e9084f24c79262409cb3349ba48946091a44b5c666d4b6af28c0947ace34627849cbb8d73ec36c908d8bcf915352576a0e3bcf1c6a85b3a4208e599509e09e539b40f7fa48752b801a361234907ab3cf37a244da2c4b914bec794e7d0e4f9dcb98dde89428265e8d0aeb274e90b8eaf0f24cf18e779dab401018f83190b9b36ade69d1990f24c28cf84242e794e1b25d085489242acffa2a294bc2e49137c7fd3529e4393e756ad6ac9a0419d422abb56adb2aeaf0f24cfa1f0ecb38f84543fca339fbc92b027d66d39e5999078ba29f8cb33af0b496879c604215dba3453b3d6e5ca954dba756b213ffde49c7e8019eb30739f5e5eb264a4b46e5dc7bb8cb266cc18240306745465162d9a4fd6af7fc9bbfd871fb6aafd311b61f9f2c565f0e0ce3ef28c148bdab5cb49f6ec99d5d4d81f7ef89a5a3f7e7c6f49972e8ddab778f102b271e314b5fef2e5add2b66d5db5be64c982b26bd71c6f591067a453b8bd5ecbf3d8b14f488102b9a4468d32de63eaf399352b3aed03d395376a54459583fd2f5d7ad7bb0db31362a6c2df7edb43794ed0cf3f21b79f28798ef55344ca3321f12cce146872abe4b971e3aad2a04165397efc6d05a6fa6ed6acbae3fea54b17f19149a434942b57dc27928ba9aca74eed2f478eac901e3d5aa9e9bc6fde343be521b5a149936a2a7de2fcf94d4ac6adf2bc66cd24f9f4d3656a8aefce9d9b78a701c7ec7aa86b9f3e6d9530eb29c021d83d7bdeaf66039c3b7788942953c43bc31eaed5fbef2f707d3dea9b3b7776e9d7afbd1c3dfaa612714c47ee1499865c4f9f3e50e5529f3dbb4ecd4068ed7c8829b4b76d9b4179a63c936420cf111106d39a28cf24b1c933059a24a43c9f39b3567dbe741416204a8c75df7cb33ed6f28c3c636b641be51d3cb850befd7683fa7bcb96a941a56dcc9b37440a17cee3987671faf41a55deba759395f8a3631fe4f5fbefb7c8b1636fa96dd6e9ac9dd2363a756aec5d5eb468b8e4cf9fd3519eebd429ef957fbb3adf75575e59b06018e599f29c24f8fb6f43fef987d7c1499e53a7a63c539e49a21367ca33494879468a03cafbeebbcdde75880663dddebdf3622dcffe39c439726451a91c7bf6cc8b713c7f79deb0e165a95af51ea950a184a250a1dc8ef2abeb5fbd7a19a959f35e2ffbf7bfaa3af0611bce27949c67a4a42072eeb41dd28cb48d88885432644817f9fdf77d3ee5219563f1e21194e72420cffffe6bc8a64d868c1e6d489f3e864c9e6cc89a3586fcf24bd2903f9cdfdab5860c1b66c8e0c186bcf9a6217ffde5bb4fddba86e7f34f51a63c539e49188933059a24a43c6bc1dcb9739677ddd6add3d53ae4ef3ac933d216f4f288118fbaca33729c511e241911e1142952a828b49d3c9f3cb94a49e9f6ed33d5f2ebaf8f7495e753a7ccc8b35d9a04f28e53a64ce9f323203ee4598372232333cbecd983bdeb90c28163bef7de6cca7398cbf38f3f1ad2a081e1f92c987239678e2103071a72cf3d86e7475be213b973e70ccf0fbbe0f7ffe927431a3634a4787143c68c3164e24443ca9737a45225b32cca33e599f24c28cf84f26c032607a958b1a4ca2d468e30c6297ee8a10652b9722995d3ab5327264e7cd2fb9a071faca7d21c90338cce829932a58f21cfc8434659c873c66b91f38cf2b11dc743273c744afcdfff5e97b2658b79e5199df120cf482781683ff248539fa8f4c0819da45ebd8a3ee7807c6dacd36914d7afeff61e0b39ce48c3707b7da8f2bc6fdf7c95db8ceb8308b9750c69a48ea023a4350f9af21c9ef2dcab9721850b1b72f9b27dc436b1895cab5686e78761f0fbf7ed6b787e98fa46d17fffdd94e7366d28cf9467ca33097371a6409384ec3088682f6419128c9122aa552bed1375468ac2dd7717f22eefde3d57f2e68d943beffc8fea583761429f18f2dca2454d2951a2a092504467ad916de43b67cc984ee526a3731f5240ac828c3253a7be43f2e4c92e0b170e57e2ad3b0d7efef90a29562cbf4a8dd0d1694876f3e6355479458ae455237ce868f38e1db3d4881e7aec65bbd7872acf783dea8b5c6c7476bc7163af771b3a2e8e1bd79b43d585b93c232a9b32a5210b16048e4e77e962783e7386e4ca6548b76ee66bf5766c9b39d390b1630dcf67ce901a350cf9f043dfd7f7e86178be4f8664c860467eadaf9d3edd90debd0dcfe7cd90ab574d916fdbd65c2e59323a023e7ebc21e9d299eb1149deb8d15cefb4bfe732a2a39bf4eb17f39c264d32a5f08b2fa2e579d42843060c30cfb3685143d6af8fdeffc001c3d34698c7c0395eba14bdcde9f876e787e5575e8929f8f3e6855e16ae15e599f24c28cf946792e0e33ca3635fb033e421a2ecb4af964d44662f5cd8ec8d60fb4f896dedc86757173d653622c9c0ba1de5fa4fa98dd134ecca849c63e48f40af0f0544b675745bf3c1078b54049f330c86bf3c1f3a648ad1679fb9efd7b8b199da71fcb849fdfa86346be61bb5cd9ddb94d4a3474df97be081e8ed489b2852041d680df9f34f438e1df37d6d64a429b3274f9a9df6b07fcf9ea61cce9d6b787e7c9afbfefaab5917e4654332fff823ba7cbbfdf5f9219fdbff9c0e1f36b721b75bd703a92b53a71a72e48829fbd9b2199e36c0dc8e1f04105744e3cf9e35cfc37a7e76c7b73bbfc58bcd48bf8eeafff0832169d244a7a28452d6adeae04879a63c93301ae89fd78f9f9dc42c5cf139e9487cb16ad584043f06462cd1c3df519ec35b9e2195388e35f7d79f33678ca8916aa2d721226b8e54132d759d3a456f5fb4c890fcf9cdbf4f9f36f79d36cdbe7cbc16a9237a59efbf6e9d29ea1f7f6c48aa54867cffbd7dda86dbfefafc7474d97f748d14290c993f3fba1e4387fa46cbcdd173cce53a750c69d22466be75a0fafa9f1f7e00207abe678fb98c2874d3a6b12b8b691b9467c2e95309092b797eedb5e77d262a2194e77093e7cf3f37c568ff7ee77d9036608e1c13bdeefc79236aa41afb7ce165cbcc28aef5f59f7ce22ccfd6d7eafdab5737a466cd68741dfde5d96d7f44b8b1cd2e2d053f18b06dfb76e79ce71c390c9931c3fc1bd28cb40da4810c1962e64d07535fbb72bb7635535ff037523374f43b3665519e29cf84f24c48d8c833a13c87bb3c23ed01a9098f3feebccf891346d44835d1ebb66e35a246aa092ccfa74e99fbea9cde40f2acf7dfb62db80e836efb6338ba3c790c69df3ee6b6d9b30d499fde8c30dbd503e914e6e839beafc30f06a44ee0f5c1d4d74e7821c9883e238d0539e48882c7b62cca33e599509e09a13c13caf32d1c6d03528bd400a40fe8fc5ee4d222e5e1cb2fcdbf2b5634a4736743ae5d333be13df49021952b47e7edbac933a857cfccddfde823735997eb2484c8afc66b748ac4f5ebe6b1f13786d1c3b660f75fbad490b4694de1b7e67a4380274cf01553e41be3fc701d30a41d7e58e872f6ed332517e75cb5aad9413298e3db9d1fca2858d08c6c6378c060cf85f24c792684f24c28cf84f29c08264941273688223aae9528618a6fcb96a6e4623b3aa74196336532a3b5d5aa45479d8391e70b17cc7d704e59b39af9d0bab39f9d1022c7b7797353ead1d110a35fe81411a49a142b668e78a1532edcf6d7f5c171d1510fa36864cc68a6635887e2433d3a768c3e7fc8b535da8e6362840b94816b73e34670f57512de9123cd9c6be439077bee9467ca3321946742792694e74420cfd63c607454731adf19290e88ccc6b6fc2b57427b3d3ad75dbc68bf0d42ee3fda84dbfe3827a44540faed46a9c0e816184103fba16cbb6b8008b08e02875adf5089cfb228cf946742792694674228cf84509e29cf84f24c08e599509e29cf84f24c792684f24c28cf84f24c79269467ca332194674279269467ca33a13c13ca33a13c13ca33a13c539e09a13c539e09e599509e09a13c134279a63c13ca33a13c1342792684f24c7926946742792684f24c08e599f24cf801a33c13ca33a13c539e09e599f24c08e599509e09e539ae9f7f42120b11119467ca33a13c13ca33218c3c131254e499f24c7926946742792684f24c08e599f24c28cf84f24c08e59910e63c539e09e599509e09a13c134279a63c13ca33af03a13c13ca33e599509e29cf84509e09e599509e29cf84f24c792684f24c28cf84f24c7926946742792694674279269467ca33219467ca33a13c13ca332194674228cf946742792649faf341c8ad9b412d82f24c08e599f24c28cf24ac3f1f911e4a416a10152424a18812e752f8cc519e09a13c539e09e59984ebe7236d94401722e41680cf5a5aca33219467ca33a13c13424858e6fc134279a63c134279268410ca33a13c539e09a13c134208e599509e29cf84509e092184f24c28cf9467ca33a13c134208e59910ca33e599509e092184f24c08e599f24c28cf841042792684f24c7926946742082194674279a63c134279268410ca33a13c539e09a13c134208e599509e09e599509e092184f24c08e599f21cabc68b902448a487b46ce408a13c134279a63cb3f122244023e8a114049a8d1c21bcff104279a63cb3f1222440231811c1469010de7f08a13c539ed978110b7fff6dc83ffff03ab0112484f71f4278dfa03cb3f1b2f0efbf866cda64c8e8d186f4e963c8e4c986ac5963c82fbf240d017ee71d439e7eda9049930cd9bd3bf8d7d6ad6bc80b2f386f3f74c890ad5bd908124278ff2184f70dca73b269bc7efcd190060d0cc997cf9061c30c9933c79081030db9e71e4376ed4a7cf53d77ce90afbf0e7eff162d0ca95ad59029530ce9dad59076ede24f9ec78d33a4478f5b7f4e6c040921ecb04ec20da6fb519e938c3cf7ea6548e1c2865cbe6c1f914e6cf56dd5ca90418382dbf7abaf0c4999d290efbe8bddb102c9f3ed3827ca3321849167c2be329467365eb7899f7e32e572c182c0d1e92e5d0cc999d3905cb90ce9d6cd7cadde8e6d33671a3276ac21050a1852a386211f7ee8fb7a4468f3e63524430643ca97f77dedf4e986f4ee6d48962c865cbd6a8a7cdbb6e672c992d111f0f1e30d4997ce5c5fbcb8211b379aeb9df6474437450a43162fb63faf962d0d59b62c7a79c912435ab7f695e751a30c1930c03cf7a2450d59bf3e7afbc489863cf144f4b2533ddcae81d339519e092194674279a63cb3f14a64206717f5fbec33f7fd1a3736533b8e1f37a95fdf9066cd7c2533776e43faf533e4e85153201f78207a7bc38686142962c8962d86fcf9a721c78ef9be3632d2cc473e79d2eca087fd7bf634457aee5c43ca9431f7fdf557b32ec8cb86a8fef14774f976fb03a4a240a01f7dd4901f7ef03dafd2a50d99352b7a79da3443ca95f3ad1bd259a64e35e4c811537eb36533e4e64d733ba2c5480bb19ea7533d9cae81d339519e09219467c29c67ca331baf44063a09a27e88d03aed73e68cb98f35228ae82bd67df34db46476ea14bd7dd12243f2e737ff3e7ddadc1762ea941a81d411bdacf75fb7ce14f58f3f3624552a43beffde3ec521d0febabe889823eafbfefba1c9f3d0a1bed1631cebe0c198f2ec568f40d780691b849070bdff20bdefafbf387a12cf93f70dca733291e7cf3f373fd0fbf73bef83d403ec63cd1b3e7fde5cb777af7d6e30522110b1b5befe934f82cb2bd6fb57af6e48cd9ad1e83afa8b66a0fdad292a10dd3469cc686fb0f2ec9ff39c2387213366c49467b77a04ba0694674248b8de7f56af36d3ce1252ce911618d772c269f4a4701c25ea8d370cf17ce478dfa03c277d79468a00d2101e7fdc799f1327cc0ffdce9dd1ebf0c5c33a74c80b24cfa74e99fbce9b17dc175defbf6d5b70a219687f2b889463df77df8d9667e45bebed2346b8cb33d23ef07adc2cfce5d9ad1e81ae01e59910921ce43936230be1e9dfd2a5092fcf8969f4a4701c250aaed0b4a929febc6f509e937cce19840ee905afbc129dcb8b473e48e9f8f24bf3ef8a150de9dcd9906bd7cc5f960f3d6448e5cad1a371b8c933a857cfccfffde823735997ebf445477e355ea3bf98d7af9bc7c6df18460fdb82d91fe9137bf6443f527ced35d56141a55160f9c107cd7413fc884067c14c9962ca33729571ceb836e820881f1bba2efe39cf6ef576bb0676e7447926842435790e355080b26bd58a9f7a86cbe849e13c4ad4b3cf3a9f3bef1b94e724d76103a351400a91d250a28429be1889028287ede8c80759865ca64f6f48b56ad151e760e4f9c205731f5c8bac59cd7c68dd31ceee8b8e3ce1e6cd4da947273b8c74a15344906a52ac9839aac7f6edeefb23d7396d5a73740bbca64a1543de7c33fa387814863ce83bef343b384e9810539e3b768cbe26e8d8688dc0fbcbb35bbdddae81dd39519e09210971ff0934ca104640426a9ad32843780287fdd18e61d4a0c1837de5192916b56b1b923dbb197cd0232f853a5a128038231d402f1f386048a346e6be682f2f5d0acfd1933032d5534f394774c375942878011ce1b7df78dfa03c27a3decef8d2225aeb34be33be486e394d81b87225b4d72337f9e245fb6d9051ff4e1176fb631fa44d38fd8a4744d9a94e1835032363e07ae078fed7058fd3faf70fadde6ed7c0ee9c28cf8490f8bcffc47594213c9a6fd2c47cc286fe2f9039ab3c6376da4f3f3583037862d9a143ec464bc2533bd4dfdac91bc3a022d50e6df1d9b366fb1c8ea327e147805ba0245c478942ca06824776e98bbc6f509e395450320791f2d75f377f8debfc690e394408492af2ec34cad0b7df9a7f43a68249db405a2026e1727ad4ef364a11440ddbac81883a75a2c53d3ecf2b318e9e14aea344dd7597fddc11bc6f509e29cfc91c342678e4867c6a8ed7c9468e90a426cf4ea30ca1cdf31f7dc95f9e376c303bbb55a86052a890b370b98d52a43bab23baadf78734236d037d57860c31e4f7dfe3e7bc12ebe849e1384a14d269ec524e78dfa03c539e0907bb67234848d8ca736c4719424412e9043a8ae92fcfe81f03b1d529097842e726cf6ea314216f161de774bf112b58873e28b367c7cf7925e6d193c2699428a476e03d7bef3dde3728cf946742d808129244ee3f711d6508a32fa15321a2a2fffb9f2165cb46cb33727921cf985c0ba2fdc823be51e950464bd2b3db62d22dbdefbe7d665e2d729e11ddb68eff1c4ea327414cad33ed5a09e751a250777424b5e6a2f3be4179a63c13c2469090b0beff0433ca10e4ca699421e43b67cc68e6c642ac904260156494e9691f244f1e43162e34c54d771a0c65b4246cdbb1c31ce1418f1d8cd7e258c8a34647c51b37c273f4a47efdcc112e9c729dc3759428743cc438d2bc6f509e29cf84b011242449dd7fdc4619d28ffd9d4619d2a32a388d28a43ba9e9082522992036a32501c839468ed0cb888aeac868521d3d291c4789fae003737415de3728cf946742d8081292acee3fb76a928d5058b58aa32725f6f3c458cf4e43f3f1be4179a63c13ca331b414292ecfd0739b6d68932387a12cf93f70dca33e59910368284f0fe4308ef1b94e7c4dc781192d4888860234808e59910ca33e5998d172141358294674278ff2184f24c7966e34508e59910de7f08a13c539ed97811c2469010de7f08e17d83f2ccc62bd180713f633ba62561234808e1fd87f0be4128cf49a6f1c220eb6bd71a326c982183079bb31de9a943831d1bf4d02143b66e6503c2469010c2fb0fe17d83509ee3b1f13a772e7a8ef984d83f547efac990860d0d295edc9031630c9938d19c2eb55225f3d8c1ca33a6ececd123eef549e8f3256c0409a13c13c2fb06e5398c1aaf56adccb9e7136aff50e9dbd79042850cf9e597e875bfff6eca739b36b77e56aa843e5fc2469010ca3321bc6f509e1359e375e080218d1a1992258b21050a1872e992b97efc7843d2a533d723d28be92eb17ee64c436ad736247b76330afce187eefb5fbe6c48dbb6e6fa9225dd678b1a3bd69c75c86e9ba7fa185541faf58bb96dd224f3cbf0c517d1f23c6a94210306189233a721458b1ab27e7df4fe88583ff144f4b25b1d7ffcd18c52631ad10c19cc48b7dbf91236828410ca33e17d83f29c841baf1a350c993eddcc253e7bd6903fff34d7fffaab218d1b1bd2a78f29977a9ef8356b0cf9f45373b97367433a7470df1f82ddb3a72157af1a3277ae2165ca387fa021f2dbb73be729a3fe9b36c5dc76f8b0b90d75d3f29c2f9f2153a71a72e48829bfd9b21972f3a6b91dd1e2162da25fef56476c2b52c4902d5bcc6b73ec98fbf91236828410ca33e17d83f29c841baf3a750c69d2c43e7737505ac2bc7986142eecbcffe9d3e68774dd3a438e1f37e4e38f0d4995ca90efbf0ffdc30e69b64697fd47d74891c290f9f3a3e579e850dfe8315e7bf0604c7976aba3de366d1ad336d808124228cf84f70dca331b2f31a519691b48891832c4cc217693c30d1b0ca95ad5900a154c9083ecb43fd21f70ccead50da959339afdfb43ffb023e28bb2162cb0efb8876d3a6a6d97f39c2387213366c49467b73aea6d9f7c427966234808a13c13de3728cf6cbc2cecdd6b4864a421b3673bcbe1c993a6646b497dfd7577793e75cafc906edb16f70f3b86a3cb93c790f6ed636e439dd3a73723cc76f2fcc30f663d56af8e29cf6e75d4db1061a73cb3112484509e09ef1b9467365eb26f9f99f6809c674494d121506f1b38d0907af57c739221cf67ce98690d8f3c62769873da1f346860aed36921d7af1b72ed9afd071ab2aa738aed58bad490b4697dc768462e34a47fc204dfd13690ab8c4e86c873460741e43cebe3fae73cbbd511eb9103fdd147e6f2975f464fc06277be848d202184f24c78dfa03c27e1c6ab5831538091bbdcb2a521376e446ffbfc73733b46e1d0d1e6071e501f3a15055eb8d09452dd69d06e7f4876f3e6661e313ade61f40b44b9ed3ed0184903a35eb87de8972d33246b56b3be1845236346331d03f26f95e78e1d0d2951c2ec3808b9deb9337abbbf3cbbd5f1c205b33c5c3b1c377ffee8ce8176e74bd808124228cf84f70dca73126fbc1065758a066b81b44e778df408bd8c282d70db5f8f4e71f162fccd328828f5575fd94fc38d5133303206f6435dac620dba7635a47fff98af73abe3952b66243b98eb43d808124228cfb7ba2de475e07d83f2ccc62bde411e34f2b43166f3bbefb20161234808b995f71f42120b1111bc6f509e29cf418121e73009cb9e3d944eca3321e416df7f223d9482b4e03b4be217dd1692c0448973297c26f9dda43c539e09e599109258ef3f69a304ba10491084d72024f0594ccbef26e599f24c28cf8410924cefefbc0e84f24c792684f24c082194674279a63c1342792684104228cf94674228cf84104208e599f24c08e599104208a13c539e09a13c1342082184f2cc41ea0907bb27841076182484f2ccc833213e9167ca332184f777ca33a13c539e09a13c134208e599509e29cf8430e799104228cf84f24c794eb6fcfdb721fffcc3eb4079268410ca33a13c539e632192efbc63c8d34f1b32699221bb77df5a99faf75f4366ce34e48d37d090c44f796bd71a326c982183071bf2e69b86fcf597ef3e75eb1af2c20bce651c3a64c8d6ad145dca332184509e09e53959cbf3b973867cfdb5efba162d0ca95ad59029530ce9dad59076eddcf70fb5fc400c1d6ac8d2a5869c386148d3a6a6ccc7f6fc7efac990860d0d295edc9031630c9938d190f2e50da954c9ac5bb0f23c6e9c213d7a24ccf5a63c539e09218410ca7398c873ab56860c1a14bdfcd55786a44c69c877df05b77fa8e50762f56a436ad58a5e7ef6d9d05eef4fdfbe86142a64c82fbf44affbfd77539edbb4095e9e13ea7a13ca33211caa9490b025d2435aca7318caf3c58b86b46e6d48d6ac665475f468432a578ede7ef9b2216ddb1a92258b21254b1ab26b97b97efc7843d2a533d72332bb71a319194d91c290c58b631ec76e7fac478a45edda8664cf6e46793ffcd07d7fa7fa008833d235ac329f3ebd21bffd662f5e63c71af2d453f6db90f211116148bf7e31b7211d05d7f28b2fa2e579d42843060c3024674e438a163564fdfae8fd11b17ee289c0d714fcf8a319a5ce9bd7900c19ccf7c4ed7a509e29cf8404dffe1f228424022c025d28b10b341b4f1b7986f8d5ab678ae6a54b66441582a6b743687bf634e4ea5543e6ce35a44c1973fdafbf1ad2b8b1217dfa9832f8c71fe67ae40643a01f7dd4901f7e882ec769ff356b0cf9f45373b97367433a7470dfdfa93ed7ae991fc4f7dff7cdbf4e95ca906ddbecc5ebc00143b66f77ce5346799b36c5dc76f8b0b90d75d7d7305f3e43a64e35e4c811537eb36533e4e64d733ba2c5486709744df5b622450cd9b2c5903fff34e4d831f7eb4179a63c1342792624fce4d922d1895aa0d978fac93384d95f1091faa0e5f9f46973fbba75861c3f6ec8c71f9b32fafdf7ee690488bae6ca65464fad321b28ed60de3c430a1776dedfad3e904c6c4324dd5ae65d7719b26041e852866b628d2efb778ac40f84f9f3a3e519b9d6d6e8315e7bf0604c79763b07bd6dda34a66d509e09a13c1392d4e5391c049a8da79f3cefdb67ca873547d92acf4827c0f6ead50da959339afdfb03cb1c3adb4118d3a431a3a64efb6fd8607630ac50c10439c64eb2e8561f7410c4b6f3e77dcb2f50c03e8d24105ac6edc41be929d8a6a3d67639cf3972183263464c79763b07bded934f28cf94674228cf842407794eec02cdc6d34f9e6fdc30e5d69aba80e8af96e753a74c39714a7b082473df7c63befedd77edf73f79d2cc2bd6c77ffd75777976ab0ff29ad15971efdee875487bc0baf7de0b5dca301c5d9e3c86b46f1f73dbecd9662e3522cc76f28c7415d4133f44fce5d9ed1cf436bc079467ca332194e743f2efbf1f78dae3f7295cb7814b97de959f7eda119675fffbef03f2cf3f07c3469e13b340b3f134ec85ac5c3943962f373bbd952e6d76ded3db1b343073a2f51069d7af9bf9c5f87be040739bde1769087bf6448f83fcda6ba61c231dc16e7fe41c63fb993366dac2238ff8e65bfbef1fa83ec8095eb4c8b73ee8080989b6132fc8aace29b60343dea54deb3b463372a123230d9930c1376f1cb9cae864883c67741044ceb3ae977fceb3db39603d72a03ffac85cfef2cbe80958ecae07e599f24c486ce51962ba76ed241936ac9b0c1edc59de7c734ca213d5d5ab277aee0b1928b3b7903fffdcefb99f56952245f24abe7c39e4c4899561770e75eb5690175ee8e9b8fdd0a1c59e7bfbf44425cf8955a0d978dac833a4b54b1743aa55333bfb21c501a90ed6edcd9b9b79b9e8c886d1247474f7f3cf0d2956ccdc1fd163e43a4336314a04d657a9624e2aa2cbf2df1feb1e7840c98f8af22e5c684aa7ee3468b7bf5b7d76ec3047a7d0633ba3531ec65876122f8ca481512fdce46cd93253c0918b8d5134326634d33130798a559e3b7634a44409b3e320e47ae7cee8edfef2ec760e172e98e5e1bdc271f3e78fee1c68773d28cf9467426223cf8828366c58598a172f2063c6f4f2fce87fd2d37e16974a95ee9673e73686a53ca3de5f7fbdfeb6d433218f1d6ad971adcbf6ed33a560c15c61fd0320903c8f1bd75b7af46875dbde77b72741894da0d9780631cef3c30f1bd2a449ccf5c85bf6ef8ca781f0e9e828fe4744d769ac67fffd75073bbd8c282c70dbdfad3eb36699a35e7cf081397a477ccd5a887342074bbb69b8316a06a2dbd80f75b58a35c06431fdfb87764daf5c719e21d1ee7a509e29cf848422cf7dfbb693428572cb2fbfecf4aefbfdf77d4a9edbb4b92f2ce5b955ab5a326850a7db52cf843c76a865c7b52e90ce7af52a266979beddef7ba034aac424d06c3c6de479f26443860c3164c50a431e7bcc1c4bd81a350d4756ad32c741beddc3b9210f1a79dc187544e77d13ca3321b75b9e7ffe79874444a4927efddac7b8694f9ad4376aa421f3517d972ecd64e6cc6764ecd827a440815c52a34619f9f0c3d71c6ffa2d5bd69265cb5ef42e2f5932525ab7aee35d467933660c9201033a4ace9c59a568d17cb27efd4bdeed3ffcb055ed9f356b461509473a89559e5197dab5cb49f6ec9955e45cd765fcf8de9efb571ab52fa2e91b374e51eb2f5fde2a6ddbd655eb4b962c28bb76cd71acfbc58beff81c7bf4e8c7a572e552deed4e65391dfbc08185d2a85115b51ed70e39c44ec776dad7a9ec84ba0e73e63cabce3f6ddad4eaf50b170ef7be6fd3a70f94debddbaa32ae5e7d4f7efc719b5a8ff73157ae6cd2ad5b0b9f1c69fdd91931e251c99327bb54ac58524e9e5ca53e1fc58ae59752a50acbe6cdaf847c4d82f99c419e478d7accf17386272d4f3cf140c0f716e03c11a5ce9b3752326448ab3e1b6ed71adf95a79e6a1f27794e4c02cdc6d3469e91a38cc942ba7737d3369c467a20a18321e730090bae31af07e59990c422cfc8f73487299d12e3867df8f0d2a871ec2779252477eeec4ab48f1e7d5309c6030fd475bce1972e5d4466cd7ac6bb3c6dda002957aeb88fd4208f76ead4fe72e4c80a2525d9b265929b37cdce5d4d9b5693264daaa947e1e7cf6f5292649567d4ebd34f97c91f7fec97ce9d9b48870e0dd5fa5f7fddadf274fbf469ab4408dbb11e62d9b3e7fd4af6e6ce1d2265ca14718d5622e2fad557ab95a821026f3db653594ec7c60f0d082772cbcf9e5da772899d8eedb4af53d909751d7efb6d8f0c1cd8496ad52aab5e7fe3c65eefb5898cccac7e5c4180d1190fc769d0a0b21c3ffeb6a27efd4ad2ac59759feb09a9465a10cea966cd7b95c43efc7013f5e30c4f3faa542915f235898fcf19a2c52d5ad40cf8deea6dc8ffdeb265aaaac3b1636fb95e6b483f525f829c2425186eab40b3f134285b84f24c4872976748b335baec3f4a418a142964fefce7bc12d2a95363eff6458b864bfefc39e324cf438776f589ea99e3e22f946fbfdda0fe86a40493b6316fde10295c388fe323f4d3a7d7448dab3f59c9ddc71fbf21a952a594efbfdf12a32c08b3ff0f0aebb1039565f7f8be4e9df2de1f0281a28c6efb064a0d88cfeb009e7fbebb1261ff1f16bd7ab5f12e9f39b35695a9a3ad00915dacfbe69bf5ded73cf9e483deed78df11b5d5a360e05aa7497367acae495c3e67fef2ec767df436947f3b5285a2e4b932a6f2a63c539e09a13c13729be4199133731cfb61b61da0cc71ec67dae68ee25139227a719167ff5cd41c39b2a8548e3d7be645cd3db0d9519e376c7859aa56bd472a5428a140deb693c8e0d1bb39ae7e1915f5d4ecdfff6a8c7aefdb37dff5d881cab29328481fd20e9022336448179553ee74dddcf6b52b3ba1ae839b3c5bdf375da6f57ae14901d6eddd3bcff635c3873faa22d57a19291b6ef2ec764de2f239f39767b7eba3b77df2c9d2db26cf111111b7f51ec7c693f24c28cf84247b79c67074c8416ddfbe418c9bf5ecd983257dfa342a52175b79c6a376bd8c7c5737a9418eb3392efe4415e943d45b4707fd0516e90210292df6afbf3ed2551a4f9d5a1335aefe8c809282f404889cf5713b22bafad881ca729328c824521e706d03d5c36e5fffb213f23a042bcf18be0e65eedc39cbbb0e43bf611da2f8f121cf6ed7242e9f337f7976bb3e7a1b3e0bb74b9e53a74e4d79a63c13427926e4768fb6b174e90baa539875ac5be4424352264ce8e3282181e4f9c107eba9340fe47fa21357a64ce963480d7248d16911f9a7e8b8855cd46bd776a9ede854868e66e878f6bfffbd2e65cb16f30a2c7249218d481980683ff248539fa8347275fd478980ac619d7ef47ffdfa6eefb1ec4408755dbe7cb4ea6c06414387bc60cab23b36a2d9488341ce2ea2c4e83ce774dddcf6f52f3ba1af4330f28cd40bbc57c8b74639783f1f7aa881ea608973880f7976bb2671fd9cf9e73cbb5d1fac470ef4471f2d51cb5f7ef95f6fea89ddb58670ebbc68ca33e59910ca3321496892148830465640be2c3a7265cc984e3dd6d6f2131b79debd7bae1a95e0ce3bffa33a1642c4fda506d252a24441550e64dd1abd44be33ea819c53080b1ecd5bc51065a64e7d878a9c6324080891ee2cf7f9e72bd4280e18994147652197cd9bd750e5a1d317465ed06905fe605f887bb56aa5d5c4318b178f506559b73b9565776c2ca3eeb8bee8f8a83bdfd9e1b6af5dd909791d8291671d01872c435cf1b402d74d479de3439eddae49309fb38e1d1b397ecefce5d9edfa5cb8b0599567cebf9051e5fcebce8176d71a9d6bd1b196f24c792684f24c48129c9e1ba28c4819a427bea63346a40f113fb7f177715c488955d4ad9d16316c9c53f94829d17545841058b7a35cff73c1c8086e65da815121d061cd7fbd5b59fec746f4d229c2eb4fa07dfdcbbe55d72110a887d3fb1d57dcae89dbe70ca36660640ca7cf59d7aecda57fff0e21bdb757ae6c773c9eddb5a63c539e09a13c139204e539a94e5e111b264feeab3aa6ad58315a1e7bec7e3586af355a49c21f7cf690238ea8f5bbef4e4ff4f5a53c539e09a13c1392cce5f9b5d79e779da8e47682d13e30c145f7ee2d55da86d3080b247cc1a81c98c004ef7538d497f29c481a4f42921a1111946742c2459e092194e7706b3c233d94826c205a4748b81325cea56ee700f284509e092194e7a4db78a68d12e842842421226fe7d4a5842437794687be84e81c4508a13c13420821494e9e0375fac378d1d6f1a3c309fc3078e79d69f2f4d30fc9a4497dd59068b7f2f8181502e319bff1c6a8041bc182509e29cf841042482292e771e37a4b8f1eade27c1c4c15ae27ad4808eccac7d8bf989063ca94a7d57066eddad58f757d6253ffa143bbaa096c30835fd3a6d594cc532229cf94674208212409cb737c91d0531ffb978fb1ae53a64c29df7db7395eea13eafe983aba56adb2dee5679f7d2441cf9f509e29cf8410424890f28c299e1b35aaa26670c3ec68972ebdebdd86d9dc3093a05ec6f4c7ad5bd7f191674c5f3d60404735131b66275cbffe25ef764c85fcc4130f78972f5fdeaa665cc3b14a962ce833541d26d840941ae3ee66c89056ca973767891b3fbeb71a6719af295ebc806cdc38c5562e30a105ea8699dff0dad1a31f57b3de053ab65df98814a7489142cd2ce87f1ca7fa20c5a276ed726a1a6f4c07fde187afb9eeef762d20ce48d7b0ca3c66eefbedb73d1449ca33e599104208b99df25ca34619993e7da0cab13d7b769d9a8d4d6f2b5dda9c16db3a36aeffd4c798f278ead4fe72e4c80a25bf981e1a33bed94d7d0ca9ecd9f37e35ebdbdcb943d4b4dbd66d980e19d372a30ec78ebde59de9ad71e3aad2a74f5b259c7a3a64bb2878bd7a159568e207409b36f7f94ce7ed746ca7f231be3304fad1475bc80fffdfdedd84585586011c5f34353a0416356686340a224a0509892648216549042291e307282e2212c48820ad8509d2508bcc81291db4c89148ccc4c27494fc42370a8220282a088a8bd09528b839f5bc792ef7deb9e7ce28e5bdd66ff14366cef59c9322fde7e579cff9e3b78a37cfd5fafcf6ed9f65274f7e9fbe5eb06056e915d9459f2fba9f788b5efc1d1d39b2a162fe3a5e17bd67cf3a21299ec5330034329e67cc783ebd7abad64cee50e2396673cb578fe3fc478f6e1c10cfe7cf6f4fc776ece8ca4e9ffe213b71e2bb148457aefc5a3a16e7bf9bb18708e6f8fdbb767d5131fa90c773bd6bd73b7faca28f1af5585a0d2f8fd9c1eea7a7e7c36cecd8d1859faf773ff143431cab7e35f4b8714f651b367c2424c5b378068046c67344738c6db4b43c905e497df3e6a13b8ae7ea99e791231fcdd6ad7b7f403cc758425c7bdab467b3e9d39f2b397cf89bd2b1a237f90d16ab870e7d9d7e7ff98c72793cd7bbf660e7bf76ad3ffd370c1fde9a56918b3ebf73e7e76983e1e4c913928e8e270befbfdefdc406c13876e9d2ae8af3c7484dad3112c4b3780680066c183c78b0276b6f7f24ebeefea0229e63a423ff7ad5aac575e339c61be2fc11aed5f17ceedcdfabadb5460ff263b1627b37f17ce3c6c114b77bf77e55b1fa9bc773bd6b0fe5fc172ffe9c7effeedd5fd6fcfcd9b3dbd20f1ff9f5376ffea46e3cd7bb9f986b8ecd8af1f7917f2fc658e27bfbf7770b49f12c9e01a091f11cabb631531b33cfb1721a1bdff26373e7be9c7576be9ae67463b3e088110f0f88e798dd8de710c79c736c108c99e798dbad35f33c73e60b692e391f11b97efdf7d267e3fb31f77bfcf8b7e9eb33677e2cbd8065c58ace74bc7a3422ae571ea8716f5bb6ac4e9b1823fc63f3de50ae5d7dfe18a53870a027bb75eb48fa7ad3a68f531cc7b845adcfc7a6cb387ee1c24f69f462e1c2d72ae6ad6bdd7fbdfb8919e9dede9515f7131b21cbe7d111cfe219001a10cfe3c78f49a11733baf1748d58c5cd8fc58b4162deb7b5f5c16cce9c97b2b56bdf1d10cff3e6bd924d98f074da38182bd7fbf6ad2f1daf8ee708cbd9b35f4cf3bdb139309ed091afb05ebefc4b3a5fdc5f84e298314f9436d79d3ad597ee334617f2d5dd18359938b1a3e2dc8b16bd9e4d9dfa4cdaec17230ef1f9a15cbbfafc31ebdcd6362c3df523be3f65caa46cebd64f4be7aa753ff1e7336cd843d9e8d18f671b37ae4c3f44e49b066b7dbedefdf4f7af4f4f0cc99fed1c1b0be399d922523c8b67006882b18d58f1cc573dabc58a72d11beee24911b11a1aabd611bff16bf9f178b9c8f2e56f0ff87d313b5cbd212e77f5eadec2ebc53586fa3af0f9f367a58d907772edf2f3c7af315e51f4ace75af7131b26f3af6325390c76ff45f713b3e6f1149363c77ad3d33b04a47816cf00d04433cfffa498838eb9df58b5cee784ff6d5d5defa50d8f7d7dabb3a54bdf4ccf562e5f05bf1f6ddbb6363d17bae8f17c8867f10c00ff81788ea7722c5bf6569a1bbe577111d75ab3e69d6cc99237d2d846d1933b403c8b670068aa7806c433008867403c03807806c4b3780600f10ce2593c03807806f12c9e01403c03e21900c433209e01403c03e21900c433209ec53300e21910cfe21900c4338867f10c00e219c4b3780600f10c88670010cf80780600f10c886700f89fc43370ff69696911cf00d080786effcba4f81f71ac6401cdef76384f8a7fbfe21900ee6d3cb7dd0ee80ee0be12ff6edbc433000034fb0fdefe100000403c03008078060000f10c0000e2190000c433000088670000403c03008078060000f10c0000e2190000c43300008867000010cf00008078060000f10c0000e2190000c43300008867000010cf00008078060000f10c0000e2190000c43300008867000010cf0000209e010000f10c0000e2190000c43300008867000010cf0000209e0100403c030000e2190000c43300008867000068567f026c50e6cbded12f9a0000000049454e44ae426082}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Note that there can be 1..* concrete subjects or observers.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Implementing Observer Pattern using TDD\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Let\u8217's use TDD to implement this pattern. Specifically, we\u8217'll be writing {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Unit_testing"}}{\fldrslt{\ul
unit tests
}}}
to test our Observer Pattern implementation as an isolated unit. Please be forwarned that I\u8217'll start out going through each and every \u8220"Red, Green, Refactor\u8221" cycle (so you can get a feel for this process), but will eventually start to condense things as we move forward. We\u8217'll be using the {\field{\*\fldinst{HYPERLINK "http://www.phpunit.de/manual/current/en/index.html"}}{\fldrslt{\ul
phpunit framework
}}}
for this chapter so make sure you have that installed if you\u8217'd like to follow along.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
require_once('Observer.php');\line
\line
class ObserverTests extends PHPUnit_Framework_TestCase\line
\{\line
public function testSubscribing()\line
\{\line
$publisher = new Subject();\line
\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And when we run phpunit we get:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 $ phpunit ObserverTest.php # outputs:\line
Fatal error: Class 'Subject' not found in\line
/Users/rlevin/programming/labs/BOOK/research/observer/ObserverTests.php on line 8\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So now we add the appropriate minimal code to get it green:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
\}\line
class ConcreteSubject extends Subject\line
\{\line
\}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And then we move on to finishing up our spec:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
class ObserverTests extends PHPUnit_Framework_TestCase\line
\{\line
public function testSubscribing()\line
\{\line
$observer = array();\line
$subject = new ConcreteSubject();\line
$subject->register($observer);\line
$this->assertEquals(1, $subject->getNumberOfObservers(),\line
"Subscriber should have 1 observer when 1 registered.");\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Running phpunit again:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 $ phpunit ObserverTest.php # outputs:\line
Fatal error: Call to undefined method ConcreteSubject::register() in \line
/Users/rlevin/programming/labs/BOOK/research/observer/ObserverTests.php on line 10\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So we add the two methods:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function register($observer)\line
\{\line
\}\line
public function getNumberOfObservers()\line
\{\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And get following:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 $ phpunit ObserverTests.php\line
Should be 1 observer when 1 added.\line
Failed asserting that null matches expected 1.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So we need to create the data structure to hold our list of observers:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
\}\line
class ConcreteSubject extends Subject\line
\{\line
private $observers = array();\line
public function register($observer)\line
\{\line
array_push($this->observers, $observer);\line
\}\line
public function getNumberOfObservers()\line
\{\line
return count($this->observers);\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Refactoring\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So now that we have our first test spec defined (and we\u8217're all green), we can take a step back and think about what in this code could be better. This is called {\b refactoring} (or {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Code_refactoring"}}{\fldrslt{\ul
Code Refactoring
}}}
). Note that we only start refactoring when our tests are green.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So, is there something about this code that strikes you as odd? It should! We have an abstract class but we\u8217're not defining any abstract methods. In this case, the {\i register} method is really part of our Subject interface so we should add that abstract method to Subject:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
abstract public function register($observer);\line
// abstract public function unregister($observer);\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Notice that we take the liberty of also adding the unregister abstract method, but we comment it out since we want to maintain a fairly fast \u8220"iterative rhythm\u8221".\par}
{\pard \ql \f0 \sa180 \li0 \fi0 You may have also noticed that we made a pretend observer object (we could have used a {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Mock_object"}}{\fldrslt{\ul
fake object
}}}
, but in this case we knew we\u8217'd soon be implementing real observers). Let\u8217's get rid of that and create a very simple but real observer now:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testSubscribing()\line
\{\line
$observer = new ConcreteObserver();\line
...\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And we get a failure saying it\u8217's not defined so\u8230?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
interface iObserver\line
\{\line
\}\line
class ConcreteObserver implements iObserver\line
\{\line
\} \line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Ok, so we\u8217've proven that we can push an object on to a PHP array. \u8220"Big deal!\u8221", you say. Sure, but we now have the benifit of having some test coverage to build on top of as we introduce more \u8220"exciting\u8221" features.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b \u8220"Hot swapping\u8221" components}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 One of the features of Observer pattern is the ability for observers to attach and detach themselves at runtime. There are many uses for this, but let\u8217's say you wanted to temporarily re-route a minimal production logging system with a more verbose logging system to track down an issue. Rather than taking down the whole system, you could simply swap out the ProductionLogger observer and swap in a VerboseLogger simply calling {\i unregister} and {\i register} respectively. Then, upon fixing the issue, you could do the reverse, and thus, swap the components back as they were.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So let\u8217's get to work on that {\i unregister} method\u8230?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testUnsubscribing()\line
\{\line
$observer1 = new ConcreteObserver();\line
$observer2 = new ConcreteObserver();\line
$subject = new ConcreteSubject();\line
$subject->register($observer1);\line
$subject->register($observer2);\line
$this->assertEquals(2, $subject->getNumberOfObservers(), \line
"Should be 2 observers when 2 added.");\line
$subject->unregister($observer1);\line
$this->assertEquals(1, $subject->getNumberOfObservers(),\line
"Should be 1 observer when 1 when one removed.");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Of course we get:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 **Fatal error: Call to undefined method ConcreteSubject::unregister()\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i At this point I\u8217'm not going to be listing every step (like when we define a class or method when we have an undefined test error just to get the test to pass) but, if you\u8217're following along, remember to take small steps between coding and running your tests.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We need to define an {\i unregister} method which we do as follows:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function unregister($observer)\line
\{\line
foreach ($this->observers as $k => $v)\line
\{\line
if ($observer == $v)\line
\{\line
unset ($this->observers[$k]);\line
return true;\line
\}\line
\}\line
return false;\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And we\u8217're all green again. Our implementation so far is:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
abstract public function register($observer);\line
abstract public function unregister($observer);\line
\}\line
\line
class ConcreteSubject extends Subject\line
\{\line
private $observers = array();\line
\line
public function register($observer)\line
\{\line
array_push($this->observers, $observer);\line
\}\line
\line
public function unregister($observer)\line
\{\line
foreach ($this->observers as $k => $v)\line
\{\line
if ($observer == $v)\line
\{\line
unset ($this->observers[$k]);\line
return true;\line
\}\line
\}\line
return false;\line
\}\line
\line
public function getNumberOfObservers()\line
\{\line
return count($this->observers);\line
\}\line
\}\line
\line
interface iObserver\line
\{\line
\}\line
class ConcreteObserver implements iObserver\line
\{\line
\} \line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Refactoring Pull Up}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 At this point, it\u8217's debatable whether Subject should be abstract or an interface since we aren\u8217't supplying any default behavior. Having a quick look, it seems we can {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Pull_Up_refactoring"}}{\fldrslt{\ul
\u8220"pull up\u8221"
}}}
the getNumberOfObservers method. We pull it up into our abstract Subject class as follows:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
protected $observers = array();\line
abstract public function register($observer);\line
abstract public function unregister($observer);\line
public function getNumberOfObservers()\line
\{\line
return count($this->observers);\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Note that getNumberOfObservers is a method that I took the liberty of creating to make the Subject more testable. It\u8217's not actually part of the Observer pattern.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b DRY (don\u8217't repeat yourself)}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Ok, that looks better but I don\u8217't like our testUnsubscribing spec. It\u8217's too long for such a simple test and also, if you look caefully at testSubscribing, you\u8217'll see that we have duplicate code breaking the {\field{\*\fldinst{HYPERLINK "http://www.artima.com/intv/dry.html"}}{\fldrslt{\ul
DRY Principle
}}}
. Essentially, the DRY principle states that we want to avoid duplication since that requires us to maintain duplicate code bases in parallel.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Setup and Teardown}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We can pull that duplicate code up in to a setUp method (setUp is used to set up state before each test case run; as expected, tearDown executes just after each test case run). Doing so will mean we have to go through and change our references from the local to class level like so:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
require_once('Observer.php');\line
\line
class ObserverTests extends PHPUnit_Framework_TestCase\line
\{\line
private $observer = null;\line
private $observer2 = null;\line
private $subject = null;\line
\line
public function setUp()\line
\{\line
$this->observer = new ConcreteObserver();\line
$this->observer2 = new ConcreteObserver();\line
$this->subject = new ConcreteSubject();\line
\}\line
public function tearDown()\line
\{\line
$this->observer = null;\line
$this->observer2 = null;\line
$this->subject = null;\line
\}\line
\line
public function testSubscribing()\line
\{\line
$observers = $this->registerMany(1);\line
$this->assertEquals(1, $this->subject->getNumberOfObservers(),\line
"Subscriber should have 1 observer when 1 observer registered.");\line
\}\line
\line
public function testUnsubscribing()\line
\{\line
$this->subject->register($this->observer);\line
$this->subject->register($this->observer2);\line
$this->assertEquals(2, $this->subject->getNumberOfObservers(),\line
"Should be 2 observers when 2 added.");\line
$this->subject->unregister($this->observer);\line
$this->assertEquals(1, $this->subject->getNumberOfObservers(),\line
"Should be 1 observer when 1 when one removed.");\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 You should now notice that\u8212-since our last refactoring\u8212-the test case has become much more readable and easy to understand. Thus we didn\u8217't just reduce duplication (but more importantly) made our tests {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Self-documenting"}}{\fldrslt{\ul
self documenting
}}}
.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 For this exercise, we\u8217're using phpunit\u8217's setUp/tearDown combination (similar to JUnit if you\u8217're a Java developer), but you\u8217'll see other similar hooks in other frameworks like beforeEach/afterEach ({\field{\*\fldinst{HYPERLINK "http://pivotal.github.com/jasmine/"}}{\fldrslt{\ul
Jasmine
}}}
), begin/done ({\field{\*\fldinst{HYPERLINK "http://qunitjs.com/"}}{\fldrslt{\ul
QUnit
}}}
), etc. These methods are called just before and after each test case allowng you to set up (and tear down) state. This ensures that each test is ran in isolation not depending on any leftover state from previous test case runs.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Helpers}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I\u8217'd like to do one more refactoring before moving on\u8212-I\u8217'm not happy with the way we\u8217're registering our observers within each test case. Let us try moving that code in to a helper function:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testUnsubscribing()\line
\{\line
$observers = $this->registerMany(5);\line
...\line
private function registerMany($n)\line
\{\line
$observers = array();\line
foreach (range(0, $n-1) as $key) \{\line
$observers[$key] = new ConcreteObserver();\line
$this->subject->register($observers[$key]);\line
\}\line
return $observers;\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i The above helper method is a direct lift from the {\b rollMany} helper function in the famous bowling game kata. If you haven\u8217't heard of it you should take a quick look at {\field{\*\fldinst{HYPERLINK "http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"}}{\fldrslt{\ul
Uncle Bob\u8217's Bowling Game Kata
}}}
. Read the short article and perhaps schedule time in your calendar to go through the power point presentation step by step. Better yet, play with the kata using your favorite language(s) in your \u8220"spare time\u8221"\u8230?it\u8217's quite instructive. Here\u8217's an example of using {\field{\*\fldinst{HYPERLINK "http://www.phpunit.de/manual/current/en/behaviour-driven-development.html#behaviour-driven-development.bowlinggame-example"}}{\fldrslt{\ul
phpunit with BDD to solve Bowling Game
}}}
.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 A Slight Detour: What should I test?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Determining just what to test is a difficult task but here are some general guidelines to help with that process.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Structured Basis Testing\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The main idea of {\field{\*\fldinst{HYPERLINK "http://www.testingstandards.co.uk/living_glossary.htm#S"}}{\fldrslt{\ul
Structured Basis Testing
}}}
is that your test cases, for a particular peice of code, are derived from the code\u8217's logic and you try to have tests in place for every possible branch. So at it\u8217's simplest:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
function foo() \{ // count 1 for the function itself\line
if (something) \{ // count 2 for this condition\line
// ...\line
\} else if (somethingElse) \{ // count 3 for this condition\line
// ...\line
\} \line
// We count one for function itself because this might be\line
// all that's executed if neither above are truthy!\line
return true;\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This is also sometimes referred to as {\i logic coverage testing}. The programmer\u8217's tome {\field{\*\fldinst{HYPERLINK "http://www.cc2e.com/Default.aspx"}}{\fldrslt{\ul
Code Complete 2
}}}
discusses this in depth in it\u8217's {\i Chapter 22: Developer Testing}.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Boundary Conditions Testing\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "http://www.informit.com/store/practice-of-programming-9780201615869"}}{\fldrslt{\ul
The Practice of Programming
}}}
tells us that we should:\par}
{\pard \ql \f0 \sa180 \li720 \fi0 Test code at its boundaries\u8230?The idea is that most bugs occur at boundaries.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 When you test production code that can take a range of valid values, use just below minimum, minimum, maximum, and just below and exceeding maximum values. This takes discipline and time (but given that boundaries are likely places where errors creep up) it\u8217's well worth the effort.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Also keep in mind that there might be compound boundaries when 2 or more variables interact. An obvious example is when you multiply two numbers. In this case, you have the potential to inadvertantly exceed the maximum value.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Known Error Testing\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Is there an area that is likely to be problemattic? If so, make sure to test for that area.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 An example might be if you were to implement a \u8220"time picker\u8221". Setting aside time zone and daylight savings concerns, we\u8217'd want heavy coverage on times known to be error prone such as: 00:00, 12:00am, 12:00pm (keep in mind the user\u8217's time format might be 12 or 24 hour format!) In addition, you may also want to use: 23:59, 11:59pm, 11:59am, 00:01, 12:01am, 12:01pm, etc.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Data-Flow Testing\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This is also discussed in the {\field{\*\fldinst{HYPERLINK "http://www.cc2e.com/Default.aspx"}}{\fldrslt{\ul
Code Complete 2
}}}
book, and you should turn to that resource for more in depth coverage. However, essentially, data-flow testing provides that {\i data usage} is just as problemattic as control flow, and that you therefore need to take into account of the various possible states your data structures might be in (e.g.\u160?Defined, Used, Killed, Entered, Exited, etc.) Data flow testing is beyond the scope of this book but you can read more in {\field{\*\fldinst{HYPERLINK "http://www.cs.swan.ac.uk/~csmarkus/CS339/dissertations/NewM.pdf"}}{\fldrslt{\ul
this paper
}}}
if you\u8217're interested.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Cross Checking\par}
{\pard \ql \f0 \sa180 \li0 \fi0 If you have to implement something that\u8217's been reliably implemented elsewhere, you may be able to {\i cross check} your results against the existing implementation:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 actual = MyMath.squareRoot(n);\line
expected = Math.sqrt(n); \line
assertEquals(actual, expected,\line
"My implementation of square root should be same as Java's");\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i In this chapter, we are focusing on using TDD to unit test our Observer implementation thus leaving out many other important types of testing: {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Integration_testing"}}{\fldrslt{\ul
Integration Testing
}}}
, {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/System_testing"}}{\fldrslt{\ul
Systems Testing
}}}
, {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Stress_testing"}}{\fldrslt{\ul
Stress Testing
}}}
, etc. In a real system, you will ideally have a suite of complimentary tests that include all of the aformentioned within an {\field{\*\fldinst{HYPERLINK "http://en.wikipedia.org/wiki/Continuous_integration"}}{\fldrslt{\ul
Continuous Integration System
}}}
. You should also consider using a tool to measure your {\field{\*\fldinst{HYPERLINK "http://martinfowler.com/bliki/TestCoverage.html"}}{\fldrslt{\ul
test code coverage
}}}
.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Testing both the \u8220"happy\u8221" and \u8220"sad\u8221" paths\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Gary Bernhardt, of {\field{\*\fldinst{HYPERLINK "https://www.destroyallsoftware.com"}}{\fldrslt{\ul
Destroy All Software
}}}
, uses \u8220"happy\u8221" and \u8220"sad\u8221" to describe the normal and abnormal code paths. At minimum, both of these paths\u8212-sometimes also called the nominal and non-nominal paths (but I like Gary\u8217's happy/sad better!)\u8212-should get proper test coverage. Perhaps so far we\u8217've been a bit too joyful!\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testSubscribingWithFalsy()\line
\{\line
$observers = $this->subject->register(null);\line
$this->assertEquals(0, $this->subject->getNumberOfObservers(),\line
"Should be 0 observers if register called with a falsy.");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Which results in:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 Failed asserting that 1 matches expected 0.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So we change our system under test to be:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function register($observer)\line
\{\line
if (!empty(\\$observer))\line
\{\line
$this->observers[] = $observer;\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And we\u8217're back to passing. However, not only could we pass in falsy arguments, but we might also pass in a non-observer as well. So we need to defend against that as well:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testSubscribingWithNonObserver()\line
\{\line
$nonObserver = "I'm not a legal observer!";\line
$observers = $this->subject->register($nonObserver);\line
$this->assertEquals(0, $this->subject->getNumberOfObservers(), \line
"Should be 0 observers if register called with a falsy.");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Obviously, we need some type hinting help here and so we try to change our abstract Subject\u8217's interface to be like:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract public function register(iObserver $observer);\line
abstract public function unregister(iObserver $observer);\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 But doing this results in the following goliath error output:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 1) ObserverTests::testSubscribingWithFalsy\line
Argument 1 passed to ConcreteSubject::register()\line
must implement interface iObserver, null given...\line
\line
2) ObserverTests::testSubscribingWithNonObserver\line
Argument 1 passed to ConcreteSubject::register()\line
must implement interface iObserver, string given...\line
...\par}
{\pard \ql \f0 \sa180 \li0 \fi0 But if we look carefully at these errors, we see that the type hinting is actually doing it\u8217's job thus disallowing us to pass both NULL and String to register! Let\u8217's remove the following two test cases:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 // public function testSubscribingWithFalsy()\line
// \{\line
// $observers = $this->subject->register(null);\line
// $this->assertEquals(0, $this->subject->getNumberOfObservers(),\line
// "Should be 0 observers when register called with a falsy.");\line
// \}\line
// public function testSubscribingWithNonObserver()\line
// \{\line
// $nonObserver = "I'm not a legal observer";\line
// $observers = $this->subject->register($nonObserver);\line
// $this->assertEquals(0, $this->subject->getNumberOfObservers(),\line
// "Should be 0 observers when register called with a falsy.");\line
// \}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 After said removal, we\u8217're back to passing again, and we\u8217've made our production code easier to read:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function register(iObserver $observer)\line
\{\line
$this->observers[] = $observer;\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Obviously, in a real system we\u8217'd need to think of other \u8220"sad paths\u8221", but it\u8217's interesting to note that the mere process of testing has helped us to remove needless code. For example, we don\u8217't have to add code like:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 if (is_object($newSubject) &&\line
$newSubject instanceof Subject) \{\line
...\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Let\u8217's continue to look for other possible \u8220"sad paths\u8221":\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testUnsubscribingWhenNone()\line
\{\line
$actual = $this->subject->unregister(new ConcreteObserver());\line
$this->assertEquals(false, $actual, \line
"Unregister returned status should be false if no observers");\line
\}\line
public function testGetNumberObserversWhenNone()\line
\{\line
$this->assertEquals(0, $this->subject->getNumberOfObservers(),\line
"Should be 0 observers when none.");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We try the above and they both pass without any changes. Since the getNumberOfObservers is really just a helper to facilitate testing (and is quite simple), let\u8217's remove that test. We only want \u8220"useful\u8221" tests.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Before we can implement our observers we need to add a capability to the subject. In order to be useful, we need to be able to set and get some sort of meaningful data from the Subject. Let\u8217's do that now:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testSubjectShouldHoldState()\line
\{\line
$stateAsString = "some state";\line
$this->subject->setState($stateAsString);\line
$actual = $this->subject->getState();\line
$this->assertEquals($stateAsString, $actual,\line
"Should get/set state as string");\line
\line
$stateAsArray = array('foo' => 'foo val');\line
$this->subject->setState($stateAsArray);\line
$actual = $this->subject->getState();\line
$this->assertSame($stateAsArray, $actual,\line
"Should get/set state as array");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And the obvious implementation:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function setState($state)\line
\{\line
$this->state = $state;\line
\}\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 I\u8217'm not pleased with the length of the test case and don\u8217't think it\u8217's too useful to test both for types String and Array. It was the first time through but not for repeatable test suite runs. So I\u8217'll remove the String part leaving just:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testSubjectShouldHoldState()\line
\{\line
$stateAsArray = array('foo' => 'foo val');\line
$this->subject->setState($stateAsArray);\line
$actual = $this->subject->getState();\line
$this->assertSame($stateAsArray, $actual,\line
"Should get/set state as array");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Again, we want readable and meaningful tests so we sometimes have to make these sorts of judgement calls, and I\u8217'm going to favor brevity in this case.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Now that we have a way to hold state, we can start implementing our observer interface:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testObserverGetsNotified()\line
\{\line
$subject = new ConcreteSubject();\line
$observer = new ConcreteObserver($subject);\line
$state = 'yogabbagabba';\line
$subject->setState($state);\line
$this->assertEquals($state, $observer->getState(),\line
"Setting state in subject notifies observer");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 And then:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
interface iObserver\line
\{\line
public function update (iSubject $subject);\line
\}\line
class ConcreteObserver implements iObserver\line
\{\line
private $state;\line
public function update (iSubject $subject)\line
\{\line
$this->state = $subject->getState();\line
\}\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
\} \line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Resulting in:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 1) ObserverTests::testObserverGetsNotified\line
Setting state in subject notifies observer\line
Failed asserting that null matches expected 'yogabbagabba'.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Our observer looks ok, but it\u8217's likely not getting notified (duh, we didn\u8217't implement that yet!)\u8230?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Implementing Observers\par}
{\pard \ql \f0 \sa180 \li0 \fi0 So we\u8217're at a sort of critical point here. We\u8217've implemented quite a bit of code, and we still have more (the notify observers routine for example). Moreover, we can feel our blood pressure rise and our confidence level dropping. What to do? Simply back up a step. Get those tests green again!\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 // public function testObserverGetsNotified()\line
// \{\line
// $subject = new ConcreteSubject();\line
// $observer = new ConcreteObserver($subject);\line
// $state = 'yogabbagabba';\line
// $subject->setState($state);\line
// $this->assertEquals($state, $observer->getState(),\line
// "Setting state in subject notifies observer");\line
// \}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We\u8217've simply commented out our last test case, re-ran our tests and we\u8217're green:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 OK, but incomplete or skipped tests!\line
Tests: 6, Assertions: 6, Incomplete: 1.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We can take a deep breath feeling reassured that we haven\u8217't created a 30 minute debugging session for ourselves. Also note that we\u8217've left the \u8220"partially implemented\u8221" changes in our production code. So we\u8217've actually made some progress given that, so far, we haven\u8217't caused a regression. We\u8217'll take a bit of a risk, and leave those change in while we implement notifications:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testObserverGetsNotified()\line
\{\line
$this->subject->register($this->observer);\line
$state = 'yogabbagabba';\line
$this->subject->setState($state);\line
$this->subject->notify();\line
$this->assertEquals($state, $this->observer->getState(),\line
"Setting state in subject notifies observer");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This was derived from the test case we commented out earlier. If it\u8217's not obvious, we\u8217've defined $this->observer in setUp. Here\u8217's our full implementation so far:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
protected $observers;\line
protected $state;\line
\line
abstract public function register(iObserver $observer);\line
abstract public function unregister(iObserver $observer);\line
\line
public function getNumberOfObservers()\line
\{\line
return count($this->observers);\line
\}\line
\}\line
\line
class ConcreteSubject extends Subject\line
\{\line
public function __construct()\line
\{\line
$this->observers = array();\line
\}\line
public function register(iObserver $observer)\line
\{\line
$this->observers[] = $observer;\line
\}\line
\line
public function unregister(iObserver $observer)\line
\{\line
foreach ($this->observers as $k => $v)\line
\{\line
if ($observer == $v)\line
\{\line
unset ($this->observers[$k]);\line
return true;\line
\}\line
\}\line
return false;\line
\}\line
\line
public function notify()\line
\{\line
foreach ($this->observers as $observer) \{\line
$observer->update($this);\line
\}\line
\}\line
public function setState($state)\line
\{\line
$this->state = $state;\line
\}\line
\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
\}\line
\line
interface iObserver\line
\{\line
public function update (Subject $subject);\line
\}\line
class ConcreteObserver implements iObserver\line
\{\line
private $state = null;\line
public function update (Subject $subject)\line
\{\line
$this->state = $subject->getState();\line
\}\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
\}\line
?> \par}
{\pard \ql \f0 \sa180 \li0 \fi0 Notice that by using a sprinkle of {\field{\*\fldinst{HYPERLINK "http://php.net/manual/en/language.oop5.typehinting.php"}}{\fldrslt{\ul
type hinting
}}}
, we don\u8217't have to resort to using things {\field{\*\fldinst{HYPERLINK "http://php.net/manual/en/function.method-exists.php"}}{\fldrslt{\ul
method_exists
}}}
checks to determine if the subject passed into {\i update} actually has a {\i getState} method\u8230?it can be safely assumed since {\i Subject} defines that as an abstract (thus required) method.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b Unique observers} We\u8217'd like to prevent an exact observer instance getting included in our subject\u8217's observer {\i List} twice. There are several approaches we could take to solve this, but let\u8217's go with adding an id property to each observer instance and then utilizing array_unique.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 First the ID part:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
// we add this test\line
public function testObserversHaveIds()\line
\{\line
$this->assertNotNull($this->observer->getID());\line
\}\line
\line
// and we add this to observer:\line
private $state = null;\line
private $id;\line
public function __construct()\line
\{\line
$this->state = null;\line
$this->id = uniqid (rand(), true);\line
\}\line
public function getID()\line
\{\line
return $this->id;\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Now that our observers have IDs, we can have our subject enforce uniqueness in {\i register}. First let\u8217's see this fail:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function testAddingDuplicates()\line
\{\line
$this->subject->register($this->observer);\line
$this->subject->register($this->observer);\line
$this->assertEquals(1, $this->subject->getNumberOfObservers(),\line
"Should only add a particular observer instance once");\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 This results in:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 1) ObserverTests::testAddingDuplicates\line
Should only add observer once\line
Failed asserting that 2 matches expected 1.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 In order to use array_uniquewe need to first implement a __toString in our observer.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 We first comment out testAddingDuplicates, verify our tests are again passing, add the {\b toString method, and once more verify our tests are still passing. After safely adding }toString, we uncomment testAddingDuplicates and continue onwards on our mission to disallow duplicates. All we have to at this point is to call array_unique when we register our observers like so:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
public function register(iObserver $observer)\line
\{\line
$this->observers[] = $observer;\line
$this->observers = array_unique($this->observers); \line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Our test is now passing and we\u8217've ensured all our observers are, in fact, unique instances.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Final Implementation\par}
{\pard \ql \f0 \sa180 \li0 \fi0 At this point, we have a fairly complete Observer implementation that could be further extended easily should we so desire.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 ObserverTests.php\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
require_once('Observer.php');\line
\line
class ObserverTests extends PHPUnit_Framework_TestCase\line
\{\line
private $subject = null;\line
private $observer = null;\line
\line
public function setUp()\line
\{\line
$this->subject = new ConcreteSubject();\line
$this->observer = new ConcreteObserver($this->subject);\line
\}\line
public function tearDown()\line
\{\line
$this->subject = null;\line
$this->observer = null;\line
\}\line
\line
public function testSubscribing()\line
\{\line
$observers = $this->registerMany(1);\line
$this->assertEquals(1, $this->subject->getNumberOfObservers(),\line
"Subscriber should have 1 observer when 1 observer registered.");\line
\}\line
\line
public function testUnsubscribing()\line
\{\line
$observers = $this->registerMany(5);\line
$this->assertEquals(5, $this->subject->getNumberOfObservers(),\line
"Should be 5 observers when 5 added.");\line
$this->subject->unregister($observers[0]);\line
$this->assertEquals(4, $this->subject->getNumberOfObservers(),\line
"Should be 4 observers when 1 when one removed.");\line
\}\line
public function testUnsubscribingWhenNone()\line
\{\line
$actual = $this->subject->unregister(new ConcreteObserver());\line
$this->assertEquals(false, $actual,\line
"Unregister returned status should be false if no observers");\line
\}\line
public function testSubjectShouldHoldState()\line
\{\line
$stateAsArray = array('foo' => 'foo val');\line
$this->subject->setState($stateAsArray);\line
$actual = $this->subject->getState();\line
$this->assertSame($stateAsArray, $actual,\line
"Should get/set state as array");\line
\}\line
public function testCreateObserver()\line
\{\line
$actual = new ConcreteObserver(new ConcreteSubject());\line
$this->assertNotNull($actual, "Should create observer");\line
\}\line
public function testObserverGetsNotified()\line
\{\line
$this->subject->register($this->observer);\line
$state = 'yogabbagabba';\line
$this->subject->setState($state);\line
$this->subject->notify();\line
$this->assertEquals($state, $this->observer->getState(),\line
"Setting state in subject notifies observer");\line
\}\line
public function testObserversHaveIds()\line
\{\line
$this->assertNotNull($this->observer->getID());\line
\}\line
public function testAddingDuplicates()\line
\{\line
$this->subject->register($this->observer);\line
$this->subject->register($this->observer);\line
$this->assertEquals(1, $this->subject->getNumberOfObservers(),\line
"Should only add a particular observer instance once");\line
\}\line
private function registerMany($n)\line
\{\line
$observers = array();\line
foreach (range(0, $n-1) as $key) \{\line
$observers[$key] = new ConcreteObserver();\line
$this->subject->register($observers[$key]);\line
\}\line
return $observers;\line
\}\line
\}\line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs24 Observer.php\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 <?php\line
abstract class Subject\line
\{\line
protected $observers;\line
protected $state;\line
\line
abstract public function register(iObserver $observer);\line
abstract public function unregister(iObserver $observer);\line
abstract public function notify();\line
\line
public function getNumberOfObservers()\line
\{\line
return count($this->observers);\line
\}\line
\}\line
\line
class ConcreteSubject extends Subject\line
\{\line
public function __construct()\line
\{\line
$this->observers = array();\line
\}\line
public function register(iObserver $observer)\line
\{\line
$this->observers[] = $observer;\line
$this->observers = array_unique($this->observers); \line
\}\line
\line
public function unregister(iObserver $observer)\line
\{\line
foreach ($this->observers as $k => $v)\line
\{\line
if ($observer == $v)\line
\{\line
unset ($this->observers[$k]);\line
return true;\line
\}\line
\}\line
return false;\line
\}\line
\line
public function notify()\line
\{\line
foreach ($this->observers as $observer) \{\line
$observer->update($this);\line
\}\line
\}\line
public function setState($state)\line
\{\line
$this->state = $state;\line
\}\line
\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
\}\line
\line
interface iObserver\line
\{\line
public function update (Subject $subject);\line
\}\line
class ConcreteObserver implements iObserver\line
\{\line
private $state = null;\line
private $id;\line
public function __construct()\line
\{\line
$this->state = null;\line
$this->id = uniqid (rand(), true);\line
\}\line
public function getID()\line
\{\line
return $this->id;\line
\}\line
public function __toString()\line
\{\line
return "id: " . $this->getID();\line
\}\line
public function update (Subject $subject)\line
\{\line
$this->state = $subject->getState();\line
\}\line
public function getState()\line
\{\line
return $this->state;\line
\}\line
\} \line
?>\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs32 Considerations\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Here are some further considerations when implementing Observer pattern that we haven\u8217't discussed\u8230?\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 SPL\par}
{\pard \ql \f0 \sa180 \li0 \fi0 It should be noted that we could have used the PHP 5 Standard Library\u8217's SplObserver and SplSubject instead of rolling our own interfaces. However, doing so was helpful in getting a clear understanding of exactly how the Observer Pattern works. If you\u8217're interested learning more about SPL, you may want to have a look at the following resources: {\field{\*\fldinst{HYPERLINK "http://php.net/manual/en/book.spl.php"}}{\fldrslt{\ul
SPL
}}}
, {\field{\*\fldinst{HYPERLINK "http://php.net/manual/en/class.splsubject.php"}}{\fldrslt{\ul
SplSubject
}}}
, and {\field{\*\fldinst{HYPERLINK "http://php.net/manual/en/class.splobserver.php"}}{\fldrslt{\ul
SplObserver
}}}
. {\i Note that instead of register/unregister they use the names attach/detach.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Push vs.\u160?Pull\par}
{\pard \ql \f0 \sa180 \li0 \fi0 It should also be noted there are different strategies for how the state is obtained by the observers. In this chapter\u8217's example, we used the {\i pull method} since our observers called:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 $subject->getState()\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Thus, upon being notified, we \u8220"pulled in\u8221" the changes from the subject. However, the inverse is also possible\u8212-where the subject {\i pushes} changes down to the observers\u8212-and, approriately enough, is called the {\i push method}. An example of how {\i push method} might work is the following:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 $observer->update($this, $user, $trackPlayed, \line
$action, [..more state info..]);\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Here, the subject has explicitly included the pertinent state information in the call to update.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The up side here is that the observers need not call:\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \f1 subject->getState()\par}
{\pard \ql \f0 \sa180 \li0 \fi0 However, there\u8217's bigger downside\u8212-loss of reuse. When we keep the state object in the subject generic enough, it\u8217's easy to reuse the Observer code (since the state object abstracts itself in a model, value object, etc.) However, if we (in contrast to using a generic state object) use a specific method signature to push state, we tightly couple our subject to the observers. In general, favor the {\i pull method} over the {\i push method}.\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Pitfalls\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i Warning: This section contains some opinionated suggestions. Use your own judgement and form your own opinions!}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 The following are some potential issues to look out for when implementing Observer pattern:\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Cyclic dependencies}: Allowing observers to set state reduces orthogonality. It is our opinion that another \u8220"impartial module\u8221" should be responsible for providing state updates to the subject. If you do need this coupling consider another design approach or implementing a Mediator to manage these interactions.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Relationship hierarchies}: Insidious coupling can occur if you allow observers to interact with each other. Strive to design observers to be completely unaware of each other.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Wasteful updates}: If an observer only has a particular {\i interest}, recieving all updates is wasteful. This can be remedied by adding an {\i interest} input to the {\i register} signature and checking on a per observer basis before broadcasting to that observer.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Spurious updates}: In a high throughput system with many observers, there are potential temporal issues. For example, a call to setState() before a previous state has been fully pushed out could result in notifications being sent to observers that have yet to receieve the previous state. The obvious workaround is to queue these states and only start notifications for the \u8220"newest state\u8221" once \u8220"previous state\u8221" notification are complete. This of course adds additional complexity.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Unpredictable ordering}: This pattern does not provide predictable ordering of observer updates and doing so increases coupling.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Inconsistent state}: If setting state is more than a simple assignment, you must ensure {\i consistent state} before broadcasting updates.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Duplicate observers}: It\u8217's easy to inadvertandly create a system where an observer is recieving \u8220"double updates\u8221" as a result of duplicates observers having been registered.\sa180\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\i We have other decisions to make when we design our implementation (who should call notify? what happens when a subject is deleted? will there be orphaned observers?). These require further research and are beyond the scope of this chapter.}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 \b \fs28 Example Uses\par}
{\pard \ql \f0 \sa180 \li0 \fi0 {\b What are some example applications that might benefit from the Observer Pattern?}\par}
{\pard \ql \f0 \sa180 \li0 \fi0 Here are a couple ideas:\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Mailing List}: Let\u8217's say you have a system that periodically adds new articles to be published. Upon a new article becoming available, the system could make a call to {\i setState} and thereafter {\i notify} allowing observers: EmailObserver, RSSFeedObserver, GooglePlusObserver, FacebookObserver, etc., to deal with the particulars of broadcasting said article as appropriate for that broadcast method.\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Login system}: Say you want provide notifications to various sub-systems when a user logs in. For example you want a \u8220"Security Observer\u8221" and perhaps a \u8220"User Stats Observer\u8221" (for the marketing department), Logger (for troubleshooting), etc. You could do so easily with the Observer pattern. {\i This article has a nice example {\field{\*\fldinst{HYPERLINK "http://www.craigsefton.com/programming/php-patterns-the-observer-pattern/"}}{\fldrslt{\ul
Login system using Observer
}}}
.}\par}
{\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab {\b Social Music}: A social music application could choose to broadcast notifications when a user plays, pauses, stops, fast forwards, seeks, etc. Perhaps your company is fortunate enough to be partnered with Facebook and needs to broadcast to the Music Dashboard via the {\field{\*\fldinst{HYPERLINK "https://developers.facebook.com/docs/technical-guides/opengraph/opengraph-tutorial/"}}{\fldrslt{\ul
Open Graph API
}}}
. But then another partner comes along and offers a similar deal to broadcast to the {\field{\*\fldinst{HYPERLINK "http://tunein.com"}}{\fldrslt{\ul
tunein
}}}