3.6. Observer¶
3.6.1. تعریف¶
دیزاین پترن Observer یک الگوی رفتاری محسوب میشه که ساختار Publish/Subscriber رو در برنامه پیاده سازی می کنه.
در واقع زمانی که یک Subject وضعیتش تغییر می کنه، Observer ها که در لیست Subscriber ها قرار دارن رو به ترتیب از تغییرات انجام شده مطلع می کنه.
کاربرد این الگو در کاهش ارتباط و وابستگی بین کلاس هاست.
3.6.2. چه زمانی استفاده میشه؟¶
این الگو زمانی استفاده میشه که نیاز هست در برنامه با تغییر وضعیت یک آبجکت، وضعیت آبجکت های دیگه ای تغییر بکنه.
همچنین زمانی استفاده میشه که یک سری آبجکت در برنامه شما باید دیگر آبجکت ها رو Observe بکنن که این موضوع میتونه در شرایط خاص یا در زمان های خاص باشه.
در این الگو چون لیست Subscriber ها داینامیک هست امکان اضافه شده و خارج شدن از این لیست در شرایط مختلف وجود داره.
3.6.3. اجزاء¶
الگوی طراحی Observer از چند بخش اصلی تشکیل میشه:
بخش اول Subject Interface هست که ساختار کلی Subject ها رو مشخص می کنه و شامل متدهایی برای register، unregister و مطلع کردن Observer هاست.
بخش بعد پیاده سازی های مربوط به Subject هاست که لیستی از Observer ها رو هم نگهداری می کنن و زمانی که تغییری رخ داد تمام Observer های register شده مطلع میشن.
بخش بعد Observer Interface هست که ساختار کلی Observer ها رو مشخص می کنه و معمولا شامل یک متد update هم هست که توسط Subject می بایست فراخوانی بشه.
بخش بعد هم پیاده سازی های Observer ها هست که مشخصه ماجراش چیه!
From Wikimedia Commons, the free media repository
Caution
✅ مزایای استفاده
رعایت اصل Open/Closed از اصول SOLID
امکان ایجاد ارتباط میان آبجکت ها در زمان اجرا
Warning
❌ معایب استفاده
اطلاع رسانی به Subscriber ها به ترتیب تصادفی صورت میگیرد
3.6.4. کاربرد عملی¶
این الگو در زبان PHP معمولا پر کاربرد هست و در فریمورک های مختلف هم دیده میشه.
به عنوان مثال در فریمورک لاراول در صورت تغییر وضعیت مدل ها، لیستی از Subscriber ها از این تغییرات مطلع میشن و موارد مورد نظر رو انجام میدن.
در PHP با پیاده سازی Interface های SplSubject و SplObserver امکان پیاده سازی این الگو وجود داره.
3.6.5. پیاده سازی¶
خب ابتدا میریم سراغ پیاده سازی Publisher یا همون Subject:
1<?php
2
3class NewsPublisher implements SplSubject
4{
5 private SplObjectStorage $observers;
6 private array $articles;
7
8 public function __construct()
9 {
10 $this->observers = new SplObjectStorage();
11 $this->articles = array();
12 }
13
14 public function attach(SplObserver $observer)
15 {
16 $this->observers->attach($observer);
17 }
18
19 public function detach(SplObserver $observer)
20 {
21 $this->observers->detach($observer);
22 }
23
24 public function notify()
25 {
26 foreach ($this->observers as $observer) {
27 $observer->update($this);
28 }
29 }
30
31 public function publishArticle($title, $content)
32 {
33 $this->articles[] = array(
34 'title' => $title,
35 'content' => $content,
36 );
37
38 $this->notify();
39 }
40
41 public function getArticles(): array
42 {
43 return $this->articles;
44 }
45}
میبینید که استفاده از SplObjectStorage کار ما رو بسیار راحت تر می کنه.
و بعد هم Subscriber یا Observer رو داریم:
1<?php
2
3// Define the observer interface
4class NewsSubscriber implements SplObserver
5{
6 private string $name;
7
8 public function __construct(string $name)
9 {
10 $this->name = $name;
11 }
12
13 public function update(SplSubject $subject)
14 {
15 $articles = $subject->getArticles();
16 echo "Hello {$this->name}, the following articles have been published:\n";
17 foreach ($articles as $article) {
18 echo "- {$article['title']} : {$article['content']}\n";
19 }
20 echo "\n";
21 }
22}
3.6.6. نحوه فراخوانی¶
1<?php
2
3// Create the news publisher and attach subscribers
4$publisher = new NewsPublisher();
5$subscriber1 = new NewsSubscriber('Saleh');
6$subscriber2 = new NewsSubscriber('Mina');
7
8$publisher->attach($subscriber1);
9$publisher->attach($subscriber2);
10
11// Publish new articles
12$publisher->publishArticle('New article 1', 'Content for new article 1');
13$publisher->publishArticle('New article 2', 'Content for new article 2');
14
15// Notify all subscribers
16$publisher->notify();
17
18// Detach a subscriber
19$publisher->detach($subscriber2);
20
21// Publish another article
22$publisher->publishArticle('New article 3', 'Content for new article 3');
23
24// Notify all subscribers
25$publisher->notify();
به همین سادگی!