ข้ามไปยังเนื้อหา

Observer

Observer นิยามการพึ่งพาแบบหนึ่ง-ต่อ-หลายระหว่าง object เพื่อว่าเมื่อ object หนึ่ง คือ subject เปลี่ยนสถานะ ผู้พึ่งพาทั้งหมดของตัวเองจะได้รับการแจ้งเตือนและอัปเดตโดยอัตโนมัติ subject เก็บรายการของ observer ไว้และกระจายข่าวสารไปยังพวกเขาโดยไม่ต้องรู้ชนิดที่เป็นรูปธรรมของพวกเขา

สมมติว่าจดหมายข่าวเผยแพร่บทความใหม่ และมีหลายสิ่งที่ต้องตอบสนอง ได้แก่ ตัวส่งอีเมล log และฟีดสดบนหน้าแรก วิธีดิบ ๆ คือให้ผู้เผยแพร่เรียกแต่ละตัวโดยตรง แต่เมื่อนั้นผู้เผยแพร่ต้อง import และรู้จักผู้บริโภคทุกตัว และทุกครั้งที่คุณเพิ่มหรือลบผู้บริโภค คุณก็ต้องแก้ผู้เผยแพร่ ผู้เผยแพร่จึงกลายเป็นศูนย์กลางที่เชื่อมติดกับทุกสิ่งที่อยู่ปลายน้ำ

ความผันแปรในที่นี้คือ ใครกำลังฟังอยู่ และกลุ่มนั้นเปลี่ยนแปลงไปตามเวลา Observer พลิกกลับความสัมพันธ์นั้น ผู้บริโภคลงทะเบียนตัวเองกับ subject และ subject กระจายข่าวสารไปยังรายการใด ๆ ที่ถืออยู่ในขณะนั้น ผู้เผยแพร่รู้จักแค่ observer interface เท่านั้น ผู้สมัครรับข่าวจึงเข้ามาและจากไปได้ในขณะรัน และผู้เผยแพร่ก็ไม่เคยเปลี่ยนแปลง

classDiagram
  class Subject {
    -observers: List~Observer~
    +subscribe(o)
    +unsubscribe(o)
    +notify(news)
  }
  class Observer {
    <<interface>>
    +update(news)
  }
  class EmailSubscriber {
    +update(news)
  }
  class FeedSubscriber {
    +update(news)
  }
  Subject o--> Observer
  Observer <|.. EmailSubscriber
  Observer <|.. FeedSubscriber
Subject กระจายการอัปเดตไปยังทุก Observer ที่ลงทะเบียนไว้
  • Subject — ถือรายการของ observer และเปิดเผยการ subscribe, unsubscribe และวิธีแจ้งเตือน เมื่อสถานะของตัวเองเปลี่ยน จะกระจายข่าวสารไปยัง observer ทั้งหมด
  • Observer — interface ที่ผู้พึ่งพาทุกตัว implement โดยทั่วไปคือ method update เดียวที่รับการเปลี่ยนแปลง
  • Concrete Observer — ผู้พึ่งพาหนึ่งตัว ตอบสนองต่อการอัปเดตในแบบของตัวเอง
  • Client — ลงทะเบียนและถอดถอน observer และกระตุ้นการเปลี่ยนสถานะบน subject

subject จดหมายข่าวที่กระจายแต่ละพาดหัวใหม่ไปยังผู้สมัครรับข่าวที่ลงทะเบียนอยู่ในขณะนั้น แต่ละภาษาใช้กลไก callback หรือ interface ที่เป็นสำนวนเฉพาะของตัวเอง

interface Observer {
update(headline: string): void;
}
class Newsletter {
private observers = new Set<Observer>();
subscribe(o: Observer): void {
this.observers.add(o);
}
unsubscribe(o: Observer): void {
this.observers.delete(o);
}
publish(headline: string): void {
for (const o of this.observers) {
o.update(headline);
}
}
}
class EmailSubscriber implements Observer {
constructor(private readonly who: string) {}
update(headline: string): void {
console.log(`Email to ${this.who}: ${headline}`);
}
}
const news = new Newsletter();
const alice = new EmailSubscriber('alice');
news.subscribe(alice);
news.subscribe(new EmailSubscriber('bob'));
news.publish('Observer explained'); // both notified
news.unsubscribe(alice);
news.publish('Now without alice'); // only bob
  • ข้อดี: subject และ observer ของตัวเองเชื่อมโยงกันอย่างหลวม subject รู้จักแค่ observer interface เท่านั้น
  • ข้อดี: observer สามารถถูกเพิ่มหรือลบในขณะรันได้ รวมถึงแบบไดนามิกขณะที่โปรแกรมทำงาน
  • ข้อดี: เป็นกระดูกสันหลังของระบบ event การ binding ของ UI และ reactive data flow
  • ข้อเสีย: ลำดับการแจ้งเตือนมักไม่ถูกระบุไว้ ดังนั้น observer ต้องไม่พึ่งพาการทำงานตามลำดับตายตัว
  • ข้อเสีย: observer ที่ไม่ถูกถอดถอนทำให้เกิด memory leak และห่วงโซ่การแจ้งเตือนที่ยาวอาจไล่ตามหรือ debug ได้ยาก
  • Mediator ก็แยกผู้ส่งออกจากผู้รับเช่นกัน แต่ส่งต่อผ่านศูนย์กลางแทนการกระจายข่าวโดยตรง
  • Command มักเป็น payload ที่ observer ได้รับ เปลี่ยนการแจ้งเตือนให้กลายเป็นแอ็กชันที่ execute ได้
ObserverMediatorCommand
ทิศทาง1 subject → N observerN peer ↔ 1 mediatorcaller → command → receiver
couplingsubject ไม่รู้จัก observerpeer ไม่รู้จักกันcaller ไม่รู้ receiver
use caseevent / state change notificationchat room, form validationundo/redo, queue, log
ตัวอย่างEventEmitter, RxJS, DOM eventsRedux store, event busbutton action, HTTP request
Observer นิยามความสัมพันธ์แบบใด?
subject รู้อะไรเกี่ยวกับ observer ของตัวเอง?
ทำไม observer จึงถูกเพิ่มและลบได้อย่างอิสระ?
สิ่งใดเป็นอันตรายจริงของ Observer?