[Design Pattern] Observer Pattern in Depth

คราวนี้มาเป็น Pattern ที่ 2 แล้วที่ผมเขียนในเรื่อง Design Pattern in Depth โดยตอนนี้ขอเขียนเกี่ยวกับ BNK48 แล้วกันครับ น้องเณอปราง ^___^

ทำไมต้องใช้ Observer Pattern

  • ถ้า Design ด้านแนวคิดที่ว่า อยากรู้ให้มาถาม (Server) สิ่งที่เกิดขึ้น คือ Client ต้องคอยวิ่งมาถาม (Request) ตรวจสอบว่าข้อมูลที่ Server มัน Update ยัง … Update ยังงง
  • ถ้าเกิดข้อมูลที่ต้องกระจายเป็นข้อมูลของวง Girl Group อย่าง BNK48 กำหนดให้เป็น Class BNK48Data ซึ่งมีหน้าที่ (Operation)
    1. Method getMemberData() – ข้อมูลสมาชิก
    2. Method getVote() – ดูผลการโหวต
    3. Method BNKDataChange() – เอาไว้ Update Data ของกับหน้าจอของ User
  • แล้วถ้าต้องเพิ่ม Dashboard สรุป stat ที่งาน (มโนไปไกลเลยเรา 555)  คือ แก้ Code ใน Class BNK48VoteDataOld !!!!!!
  • มาดู Class Diagram แบบแรกกันครับ
  • !!!! เราทำอย่างไรให้ Class BNK48Data นั้น Clean ที่สุด แก้อะไรก็แล้วเพิ่มแล้ว ไม้ต้องไปสั่ง Update ให้ Client ด้วย ถ้ามีดูที่ Class BNKVoteDateOld Method BNK48DataChange() มันต้องไป Update Display ทั้ง 2 เอง ตาม Code ดังนี้
public class BNK48VoteDataOld {
   // instance variable declarations
   // ....
   public void BNK48DataChanged() {
      // Some logic ...
      userDisplay.update(MemberData, CurrentVote);
      dashboardStatDisplay.update(MemberData, CurrentVote);
   }
   // other methods ...
}

เมื่อไหร่ควรจะใช้

  • ตอนที่จำเป็นต้องกระจายข้อมูลเยอะให้กับผู้รับที่มีจำนวนเยอะมากๆ (ONE TO MANY) อาทิ และไม่อยากให้ Server รับภาระให้การ Push ข้อมูลไปที่ Client เมื่อมีการเปลี่ยนแปลง เช่น
    • ตลาดหุ้น ต้องการส่งการแจ้งเตือนของราคาตลาดที่เปลี่ยนแปลง ไปยังโปรแกรม Trade หุ้นที่ได้ลงะทะบียนไว้ (Subscribe)
    • วงไอดอล BNK48 ต้องการแจ้งเตือนข้อมูลการออก Event ในแต่ละวันให้กับเหล่าโอดะที่ติดตามอยู่
  • สิ่งที่ได้ -  ลด coupling ระหว่าง class เพราะไม่ต้องรู้ว่ามีตัว Observer ทั้งหมดกี่รูปแบบ

Pattern มันเป็นอย่างไร - Class Diagram

  • ใช้ Observer แล้วเป็นอย่างไร มาดูกัน ภาระของ Subject BNK48VoteData ลดลงไปครับ
  • ส่วนประกอบที่สำคัญกันบ้าง
    • Subject - แหล่งข่าว (ONE)
    • Observer - ผู้สังเกตุการณ์ (MANY)

มุมมองตอน Runtime - Object Diagram

  • อันนี้ไม่ขออธิบายเยอะครับ รูปมันบอกอยู่แล้ว

มุมมองลำดับการทำงาน - Sequence Diagram

  • ตอน Register Observer ถ้าดูจาก Code ด้านล่างเป็น Method registerObserver(Observer o)
  • ตอน Notify
  • ตอน Notify - อันนี้ใช้ Loop เพราะถ้าของจริง คงไม่มี Display แค่ 2 อันครับ ถ้าดูจาก Code ด้านล่างเป็น Method notifyObservers()

ปิดท้ายด้วย Source Code (อาจจะรันไม่ได้ Code เทพ No Complier 5555) เพื่อให้เห็นภาพรวมตาม Sequence Diagram

  • ส่วนของ Subject
public class BNK48VoteData implements Subject {
   // instance variable declarations
   private ArrayList observers;
   // Other Variable ..

   public BNK48VoteData() {
      observers = new ArrayList();
   }

   public void registerObserver(Observer o) {
      observers.add(o);
   }

   public void removeObserver(Observer o) {
      int i = observers.indexOf(o);
      if (i >= 0) {
         observers.remove(i);
      }
   }

   public void notifyObservers() {
      for (int i = 0; i < observers.size(); i++) {
         Observer observer = (Observer)observers.get(i);
         observer.update(MemberData, CurrentVote);
      } 
   }

 
  public void BNK48DataChanged() {
      //เห็นว่า Code มันจะไม่รู้จักกับ Display ตรงๆนะครับ มันรู้จักกับ Interface Observer แทน
      notifyObservers();
   }

   public void getBNKData() {
      //Set Data .....

      //NotifyChange
      BNK48DataChanged();
   }
}
  • ส่วนของ Observer
import java.util.Observable;
import java.util.Observer;
public class UserDisplay implements Observer, DisplayStyle {
   Observable observable;
   //Other Local Data
   public UserDisplay(Observable observable) {
      this.observable = observable;
      observable.addObserver(this);
   }
   public void update(Observable obs, Object arg) {
      if (obs instanceof BNK48VoteData) {
         BNK48VoteData  data = (BNK48VoteData)obs;
         //Do Something ...
         display();
      }
   }

   public void display() {
      //Some implementation here!!!
   }
}

หมายเหตุ

  • ทำไปทำมา ไม่น่าตั้งชื่อ Class เป็น BNK48 เลย มันดูเฉพาะเจาะจงมาก น่าจะปรับเป็น Class MemberVoting แทน จะได้เอาไปใช้กับหลายๆกรณีที่คล้ายคลึงกันครับ