เมื่อใดที่เราควรใช้นักสังเกตการณ์และสังเกตได้


200

ผู้สัมภาษณ์ถามฉัน:

คืออะไรObserverและObservableและเมื่อเราควรใช้พวกเขา?

ฉันไม่ทราบข้อกำหนดเหล่านี้ดังนั้นเมื่อฉันกลับถึงบ้านและเริ่ม Googling เกี่ยวกับObserverและObservableฉันพบบางจุดจากแหล่งข้อมูลที่แตกต่างกัน:

1) Observableเป็นคลาสและObserverเป็นอินเทอร์เฟซ

2) Observableระดับเก็บรายการของObservers

3) เมื่อมีการObservableอัปเดตวัตถุวัตถุจะเรียกใช้update()วิธีการของแต่ละวัตถุObserverเพื่อแจ้งว่ามีการเปลี่ยนแปลง

ฉันพบตัวอย่างนี้:

import java.util.Observable;
import java.util.Observer;

class MessageBoard extends Observable
{
    public void changeMessage(String message) 
    {
        setChanged();
        notifyObservers(message);
    }
}

class Student implements Observer 
{
    @Override
    public void update(Observable o, Object arg) 
    {
        System.out.println("Message board changed: " + arg);
    }
}

public class MessageBoardTest 
{
    public static void main(String[] args) 
    {
        MessageBoard board = new MessageBoard();
        Student bob = new Student();
        Student joe = new Student();
        board.addObserver(bob);
        board.addObserver(joe);
        board.changeMessage("More Homework!");
    }
}

แต่ฉันไม่เข้าใจว่าทำไมเราต้องการObserverและObservable? อะไรsetChanged()และnotifyObservers(message)วิธีการมีไว้เพื่ออะไร?


ลิงก์ไม่ทำงาน @Androider คุณสามารถให้ลิงค์ที่อัพเดทได้ไหม?
prateek

หากคุณใช้ Java 6 ขึ้นไปลองใช้dzone.com/articles/java-ee6-events-lightweight
Ramiz Uddin

1
ฉันขอแนะนำให้อ่านหนังสือเล่มนี้เพื่อทำความเข้าใจรูปแบบการออกแบบที่ดี มันเจอโง่ แต่มันเป็นเครื่องมือการเรียนรู้ที่ยอดเยี่ยม
countofmontecristo

1
ทุกคนโปรดทราบว่า; ผู้สังเกตการณ์ / สังเกตการณ์เลิกใช้แล้วใน Java 9 ทางเลือก: stackoverflow.com/questions/46380073/…
eren130

คำตอบ:


265

คุณมีตัวอย่างที่ชัดเจนของ Student และกระดานข้อความ นักเรียนลงทะเบียนโดยการเพิ่มตัวเองลงในรายการของผู้สังเกตการณ์ที่ต้องการรับการแจ้งเตือนเมื่อมีการโพสต์ข้อความใหม่ไปยังกระดานข้อความ เมื่อมีการเพิ่มข้อความใน MessageBoard จะวนซ้ำในรายการ Observers และแจ้งให้ทราบว่าเกิดเหตุการณ์ขึ้น

คิดว่า Twitter เมื่อคุณบอกว่าคุณต้องการติดตามใครบางคน Twitter จะเพิ่มคุณในรายการผู้ติดตาม เมื่อพวกเขาส่งทวีตใหม่คุณจะเห็นในการป้อนข้อมูลของคุณ ในกรณีนี้บัญชี Twitter ของคุณคือผู้สังเกตการณ์และบุคคลที่คุณติดตามคือผู้สังเกตการณ์

การเปรียบเทียบอาจไม่สมบูรณ์แบบเนื่องจาก Twitter มีแนวโน้มที่จะเป็นสื่อกลางมากกว่า แต่มันแสดงให้เห็นถึงจุด


57

ในคำศัพท์ง่าย ๆ (เพราะคำตอบอื่น ๆ จะอ้างอิงคุณถึงรูปแบบการออกแบบที่เป็นทางการอยู่แล้วดังนั้นให้ดูที่รายละเอียดเพิ่มเติม)

หากคุณต้องการมีคลาสที่ตรวจสอบโดยคลาสอื่น ๆ ในระบบนิเวศของโปรแกรมของคุณคุณบอกว่าคุณต้องการให้คลาสนั้นเป็นที่สังเกตได้ เช่นอาจมีการเปลี่ยนแปลงบางอย่างในสถานะที่คุณต้องการออกอากาศไปยังส่วนที่เหลือของโปรแกรม

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

ดังนั้นคลาส Observable จะมีรายการ Observers (เช่นอินสแตนซ์ที่ใช้เมธอดอินเตอร์เฟส Observer ที่คุณอาจมี) เมื่อใดก็ตามที่มันต้องการถ่ายทอดบางสิ่งมันก็แค่เรียกวิธีนี้กับผู้สังเกตการณ์ทุกคนทีละคน

สิ่งสุดท้ายที่ต้องปิดตัวต่อคือคลาสที่สังเกตได้จะรู้ได้อย่างไรว่าใครสนใจ ดังนั้นชั้นสังเกตได้จะต้องเสนอกลไกบางอย่างเพื่อให้ผู้สังเกตการณ์ลงทะเบียนความสนใจ วิธีการเช่นaddObserver(Observer o)เพิ่ม Observer ภายในรายการของผู้สังเกตการณ์ดังนั้นเมื่อสิ่งที่สำคัญเกิดขึ้นมันจะวนรอบรายการและเรียกวิธีการแจ้งเตือนที่เกี่ยวข้องของอินเทอร์เฟซผู้สังเกตการณ์ของแต่ละอินสแตนซ์ในรายการ

อาจเป็นได้ว่าในการสัมภาษณ์พวกเขาไม่ได้ถามคุณอย่างชัดเจนเกี่ยวกับjava.util.Observerและjava.util.Observableแต่เกี่ยวกับแนวคิดทั่วไป แนวคิดคือรูปแบบการออกแบบซึ่ง Java เกิดขึ้นเพื่อให้การสนับสนุนโดยตรงนอกกรอบเพื่อช่วยให้คุณสามารถใช้งานได้อย่างรวดเร็วเมื่อคุณต้องการ ดังนั้นฉันขอแนะนำให้คุณเข้าใจแนวคิดมากกว่าวิธี / ชั้นเรียนจริง (ซึ่งคุณสามารถค้นหาเมื่อคุณต้องการ)

UPDATE

เพื่อตอบความคิดเห็นของคุณjava.util.Observableชั้นเรียนจริงมีสิ่งอำนวยความสะดวกดังต่อไปนี้:

  1. การดูแลรักษารายการjava.util.Observerอินสแตนซ์ สามารถเพิ่มอินสแตนซ์ใหม่ที่สนใจในการแจ้งเตือนผ่านaddObserver(Observer o)และลบออกdeleteObserver(Observer o)ได้

  2. การบำรุงรักษาสถานะภายในโดยระบุว่าวัตถุนั้นมีการเปลี่ยนแปลงตั้งแต่การแจ้งเตือนครั้งล่าสุดแก่ผู้สังเกตการณ์หรือไม่ สิ่งนี้มีประโยชน์เพราะแยกส่วนที่คุณพูดว่าObservableมีการเปลี่ยนแปลงจากส่วนที่คุณแจ้งการเปลี่ยนแปลง (เช่นมีประโยชน์หากคุณมีการเปลี่ยนแปลงหลายอย่างเกิดขึ้นและคุณต้องการแจ้งให้ทราบเมื่อสิ้นสุดกระบวนการแทนในแต่ละขั้นตอนเล็ก ๆ ) setChanged()นี้จะกระทำผ่าน ดังนั้นคุณเพียงแค่เรียกมันว่าเมื่อคุณเปลี่ยนบางสิ่งบางอย่างไปObservableและคุณต้องการให้คนที่เหลือObserversรู้ในที่สุด

  3. แจ้งผู้สังเกตการณ์ทั้งหมดว่าสถานะเฉพาะObservableมีการเปลี่ยนแปลง notifyObservers()นี้จะกระทำผ่าน การตรวจสอบนี้จะตรวจสอบว่าวัตถุมีการเปลี่ยนแปลงจริง ๆ (เช่นการโทรไปsetChanged()) ก่อนที่จะดำเนินการแจ้งเตือน มี 2 ​​รุ่นหนึ่งรุ่นไม่มีอาร์กิวเมนต์และอีกรุ่นหนึ่งมีObjectอาร์กิวเมนต์ในกรณีที่คุณต้องการส่งข้อมูลเพิ่มเติมด้วยการแจ้งเตือน ภายในสิ่งที่เกิดขึ้นคือมันแค่ทำซ้ำผ่านรายการObserverอินสแตนซ์และเรียกใช้update(Observable o, Object arg)วิธีการสำหรับแต่ละรายการ สิ่งนี้จะบอกว่าObserverวัตถุใดที่สามารถสังเกตได้ซึ่งเปลี่ยนไป (คุณสามารถสังเกตได้มากกว่าหนึ่งวัตถุ) และสิ่งพิเศษที่Object argจะนำมาซึ่งข้อมูลเพิ่มเติมบางอย่าง (ผ่านไปnotifyObservers()แล้ว)


37

คำนิยาม

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

ตัวอย่าง

  1. สมมติว่าที่อยู่ถาวรของคุณมีการเปลี่ยนแปลงจากนั้นคุณจะต้องแจ้งเจ้าหน้าที่หนังสือเดินทางและเจ้าหน้าที่บัตรแพน ดังนั้นที่นี่ผู้มีอำนาจหนังสือเดินทางและผู้มีอำนาจบัตรแพนเป็นผู้สังเกตการณ์และคุณเป็นเรื่อง

  2. บน Facebook เช่นกันหากคุณสมัครสมาชิกใครก็ตามเมื่อใดก็ตามที่มีการอัพเดตใหม่เกิดขึ้นคุณจะได้รับแจ้ง

ควรใช้เมื่อใด:

  1. เมื่อวัตถุหนึ่งเปลี่ยนสถานะของวัตถุวัตถุอ้างอิงทั้งหมดอื่น ๆ จะต้องเปลี่ยนสถานะของตนโดยอัตโนมัติเพื่อรักษาความมั่นคง

  2. เมื่อผู้ทดสอบไม่ทราบจำนวนผู้สังเกตการณ์ก็มี

  3. เมื่อวัตถุควรจะสามารถแจ้งเตือนวัตถุอื่น ๆ โดยไม่ทราบว่าใครเป็นวัตถุ

ขั้นตอนที่ 1

สร้างคลาสหัวเรื่อง

Subject.java

  import java.util.ArrayList;
  import java.util.List;

  public class Subject {

  private List<Observer> observers 
        = new ArrayList<Observer>();
  private int state;

  public int getState() {
    return state;
  }

 public void setState(int state) {
   this.state = state;
   notifyAllObservers();
 }

   public void attach(Observer observer){
     observers.add(observer);       
   }

  public void notifyAllObservers(){
    for (Observer observer : observers) {
     observer.update();
  }
}   

}

ขั้นตอนที่ 2

สร้างคลาสผู้สังเกตการณ์

Observer.java

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

ขั้นตอนที่ 3

สร้างคลาสผู้สังเกตการณ์ที่เป็นรูปธรรม

BinaryObserver.java

public class BinaryObserver extends Observer{

  public BinaryObserver(Subject subject){
     this.subject = subject;
     this.subject.attach(this);
  }

  @Override
  public void update() {
     System.out.println( "Binary String: " 
     + Integer.toBinaryString( subject.getState() ) ); 
  }

}

OctalObserver.java

public class OctalObserver extends Observer{

   public OctalObserver(Subject subject){
     this.subject = subject;
    this.subject.attach(this);
 }

  @Override
  public void update() {
    System.out.println( "Octal String: " 
    + Integer.toOctalString( subject.getState() ) ); 
  }

}

HexaObserver.java

public class HexaObserver extends Observer{

  public HexaObserver(Subject subject){
    this.subject = subject;
    this.subject.attach(this);
 }

  @Override
  public void update() {
     System.out.println( "Hex String: " 
    + Integer.toHexString( subject.getState() ).toUpperCase() ); 
}

}

ขั้นตอนที่ 4

ใช้วัตถุและวัตถุผู้สังเกตการณ์ที่เป็นรูปธรรม

ObserverPatternDemo.java

 public class ObserverPatternDemo {
    public static void main(String[] args) {
       Subject subject = new Subject();

       new HexaObserver(subject);
       new OctalObserver(subject);
       new BinaryObserver(subject);

       System.out.println("First state change: 15");    
       subject.setState(15);
       System.out.println("Second state change: 10");   
       subject.setState(10);
 }

}

ขั้นตอนที่ 5

ตรวจสอบผลลัพธ์

การเปลี่ยนสถานะครั้งแรก: 15

สตริง Hex: F

สตริง Octal: 17

สตริงไบนารี: 1111

การเปลี่ยนแปลงสถานะที่สอง: 10

สตริง Hex: A

สตริง Octal: 12

สตริงไบนารี: 1010


อธิบายเป็นอย่างดี :)
roottraveller

3
ฉันคิดว่า "Defination" เป็นตัวพิมพ์ผิด ฉันหวังว่ามันเป็นตัวพิมพ์ผิด
JohnJohn

10

พวกเขาเป็นส่วนหนึ่งของรูปแบบการออกแบบสังเกตการณ์ มักจะหนึ่งหรือมากกว่าoberversได้รับทราบเกี่ยวกับการเปลี่ยนแปลงในหนึ่งที่สังเกตได้ มันเป็นสัญญลักษณ์ที่ "บางอย่าง" เกิดขึ้นซึ่งคุณในฐานะโปรแกรมเมอร์สามารถกำหนดความหมายของ "บางสิ่ง" ได้

เมื่อใช้รูปแบบนี้คุณจะต้องแยกเอนทิตีทั้งสองออกจากกัน - ผู้สังเกตการณ์สามารถเสียบปลั๊กได้


ฉันจะขอบคุณถ้าคุณจะเพิ่มคำอธิบายboard.changeMessage("More Homework!");ในคำตอบของคุณฉันหมายถึงสิ่งที่เกิดขึ้นเมื่อchangeMessage("More Homework!");ถูกเรียก
Ravi

9

ผู้สังเกตการณ์หรือที่รู้จักว่ามีการลงทะเบียนโทรกลับที่

มันใช้สำหรับการแจ้งเช่นเกี่ยวกับเหตุการณ์ที่เกิดขึ้นในบางช่วงเวลา มันถูกใช้อย่างกว้างขวางใน Swing, Ajax, GWT สำหรับการดำเนินการเยี่ยงอย่างเช่นเหตุการณ์ UI (การคลิกปุ่ม, เปลี่ยนฟิลด์ข้อความ ฯลฯ )

ใน Swing คุณจะพบวิธีการต่าง ๆ เช่น addXXXListener (Listener l) ใน GWT คุณมี callbacks (Async)

เนื่องจากรายการผู้สังเกตการณ์เป็นแบบไดนามิกผู้สังเกตการณ์สามารถลงทะเบียนและถอนการลงทะเบียนระหว่างรันไทม์ นอกจากนี้ยังเป็นวิธีที่ดีในการแยกการสังเกตจากผู้สังเกตการณ์เมื่อมีการใช้อินเตอร์เฟส


9

หากผู้สัมภาษณ์ขอให้ใช้รูปแบบการออกแบบของ Observerโดยไม่ใช้คลาสและอินเตอร์เฟสของ Observer คุณสามารถใช้ตัวอย่างง่ายๆดังต่อไปนี้!

MyObserver เป็นอินเตอร์เฟสผู้สังเกตการณ์

interface MyObserver {

    void update(MyObservable o, Object arg);
}

MyObservable เป็นคลาสที่สังเกตได้

class MyObservable
{
    ArrayList<MyObserver> myObserverList = new ArrayList<MyObserver>();

    boolean changeFlag = false;

    public void notifyObservers(Object o)
    {
        if (hasChanged())
        {
            for(MyObserver mo : myObserverList) {
                mo.update(this, o);
            }
            clearChanged();
        }
    }


    public void addObserver(MyObserver o) {
        myObserverList.add(o);        
    }

    public void setChanged() {
        changeFlag = true;
    }

    public boolean hasChanged() {
        return changeFlag;
    }

    protected void clearChanged() {
        changeFlag = false;
    }

    // ...
}

ตัวอย่างของคุณกับ MyObserver และ MyObservable!

class MessageBoard extends MyObservable {
  private String message;

  public String getMessage() {
    return message;
  }

  public void changeMessage(String message) {
    this.message = message;
    setChanged();
    notifyObservers(message);
  }

  public static void main(String[] args) {
    MessageBoard board = new MessageBoard();
    Student bob = new Student();
    Student joe = new Student();
    board.addObserver(bob);
    board.addObserver(joe);
    board.changeMessage("More Homework!");
  }
}

class Student implements MyObserver {

  @Override
  public void update(MyObservable o, Object arg) {
    System.out.println("Message board changed: " + arg);
  }

}

5

"ฉันพยายามคิดออกว่าทำไมเราจึงต้องมีผู้สังเกตการณ์และสังเกตได้"

ตามคำตอบก่อนหน้านี้ที่ระบุไว้แล้วพวกเขามีวิธีการสมัครสมาชิกผู้สังเกตการณ์เพื่อรับการแจ้งเตือนอัตโนมัติของสิ่งที่สังเกตได้

แอปพลิเคชันตัวอย่างหนึ่งซึ่งสิ่งนี้อาจมีประโยชน์อยู่ในการเชื่อมโยงข้อมูลสมมติว่าคุณมี UI บางอย่างที่แก้ไขข้อมูลบางส่วนและคุณต้องการให้ UI ตอบสนองเมื่อมีการอัปเดตข้อมูลคุณสามารถทำให้ข้อมูลของคุณสามารถสังเกตได้ ข้อมูล

Knockout.js เป็นเฟรมเวิร์กจาวาสคริปต์ MVVM ที่มีการเริ่มต้นการสอนที่ยอดเยี่ยมเพื่อดูสิ่งที่สังเกตได้ในการทำงานจริง ๆ ฉันแนะนำให้ทำแบบฝึกหัดนี้ http://learn.knockoutjs.com/

ฉันยังพบบทความนี้ในหน้าเริ่มต้น Visual Studio 2008 ( รูปแบบการสังเกตการณ์เป็นรากฐานของการพัฒนาตัวควบคุม Model View (MVC) ) http://visualstudiomagazine.com/articles/2013/08/14/the-observer-pattern-in -net.aspx


3

ฉันเขียนคำอธิบายสั้น ๆ เกี่ยวกับรูปแบบผู้สังเกตการณ์ที่นี่: http://www.devcodenote.com/2015/04/design-patterns-observer-pattern.html

ตัวอย่างจากโพสต์:

รูปแบบการสังเกตการณ์: มันสร้างความสัมพันธ์แบบหนึ่ง - ต่อ - กลุ่มระหว่างวัตถุและมีการออกแบบที่เชื่อมโยงกันอย่างอิสระระหว่างวัตถุที่พึ่งพาซึ่งกันและกัน

นิยามหนังสือเรียน: รูปแบบการสังเกตการณ์กำหนดการพึ่งพาแบบตัวต่อตัวระหว่างวัตถุต่าง ๆ เพื่อที่ว่าเมื่อวัตถุหนึ่งเปลี่ยนแปลงสถานะผู้ติดตามทั้งหมดจะได้รับการแจ้งเตือนและอัพเดตโดยอัตโนมัติ

พิจารณาบริการแจ้งเตือนฟีดเช่น แบบจำลองการบอกรับเป็นสมาชิกที่ดีที่สุดที่จะเข้าใจรูปแบบการสังเกต


0

รูปแบบการสังเกตการณ์จะใช้เมื่อมีความสัมพันธ์แบบหนึ่งต่อหลายคนระหว่างวัตถุเช่นหากมีการปรับเปลี่ยนวัตถุหนึ่งวัตถุวัตถุที่อยู่ในนั้นจะได้รับการแจ้งเตือนโดยอัตโนมัติ


0

เนื่องจาก Java9 ทั้งสองอินเตอร์เฟสเลิกใช้แล้วหมายความว่าคุณไม่ควรใช้อีกต่อไป ดูObserver เลิกใช้แล้วใน Java 9 เราควรใช้อะไรแทนมัน

อย่างไรก็ตามคุณยังอาจได้รับคำถามสัมภาษณ์เกี่ยวกับพวกเขา ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.