Коли ми повинні використовувати Спостережник і Спостережуване?


200

Інтерв'юер запитав мене:

Що таке Observerі Observableколи ми повинні їх використовувати?

Я не знав про ці умови, тому , коли я повернувся додому і почав Googling про Observerі Observableя виявив деякі моменти з різних ресурсів:

1) Observable- клас і Observerє інтерфейсом.

2) ObservableКлас підтримує список Observers.

3) Коли Observableоб’єкт оновлюється, він викликає update()метод кожного з його Observers, щоб повідомити, що він змінюється.

Я знайшов такий приклад:

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
Раміз Уддін,

1
Я б настійно пропонував прочитати цю книгу, щоб добре зрозуміти шаблони дизайну. Це трапляється як нерозумно, але це відмінний інструмент для навчання.
countofmontecristo

1
Всі, будь ласка, зверніть увагу на це; Спостерігач / Спостережувані є застарілим в Java 9. Альтернативи: stackoverflow.com/questions/46380073 / ...
eren130

Відповіді:


265

У вас є конкретний приклад студента та дошки оголошень. Студент реєструється, додаючи себе до списку спостерігачів, які хочуть отримувати сповіщення, коли нове повідомлення розміщене на дошці повідомлень. Коли повідомлення додається до дошки оголошень, воно повторює список своїх спостерігачів та повідомляє їх, що подія сталася.

Подумайте Twitter. Коли ви говорите, що хочете наслідувати когось, Twitter додає вас до їх списку послідовників. Коли вони надіслали новий твіт, ви побачите його у своєму вкладі. У такому випадку ваш обліковий запис Twitter - це Спостерігач, а людина, яку ви стежите, - спостерігач.

Аналогія може бути не ідеальною, оскільки швидше за все Twitter є посередником. Але це ілюструє суть.


57

Дуже простими словами (оскільки інші відповіді так чи інакше стосуються вас всіх офіційних моделей дизайну, тому подивіться на них для отримання детальної інформації):

Якщо ви хочете мати клас, який контролюється іншими класами в екосистемі вашої програми, ви говорите, що хочете, щоб цей клас був спостережливим. Тобто в його стані можуть бути якісь зміни, які ви хотіли б транслювати на решту програми.

Тепер для цього нам потрібно закликати якийсь метод. Ми не хочемо, щоб спостерігаючий клас був тісно поєднаний з класами, які зацікавлені спостерігати за ним. Байдуже, хто це, доки він відповідає певним критеріям. (Уявіть, що це радіостанція, байдуже, хто слухає, доки у них FM-радіо налаштоване на їх частоту). Для цього ми використовуємо інтерфейс, який називають спостерігачем.

Таким чином, клас Observable матиме список спостерігачів (тобто екземпляри, що реалізують методи інтерфейсу Observer, які ви могли мати). Щоразу, коли він хоче щось транслювати, він просто викликає метод всіх спостерігачів, один за одним.

Останнє, що слід закрити головоломку - як дізнається клас спостерігачів, хто цікавиться? Таким чином, клас спостереження повинен запропонувати певний механізм, який дозволить спостерігачам зареєструвати свій інтерес. Метод, такий як addObserver(Observer o)внутрішньо, додає спостерігача до списку спостерігачів, так що коли щось важливе відбувається, він переходить через список і викликає відповідний метод сповіщення інтерфейсу спостерігача кожного екземпляра у списку.

Може бути, в інтерв'ю вони не просили вас явно про java.util.Observerі java.util.Observableа про загальної концепції. Концепція - це модель дизайну, в якій Java, як правило, надає підтримку безпосередньо з коробки, щоб допомогти вам швидко реалізувати її, коли вам це потрібно. Тому я б запропонував вам зрозуміти концепцію, а не фактичні методи / класи (які ви можете шукати, коли вони вам потрібні).

ОНОВЛЕННЯ

У відповідь на ваш коментар, фактичний java.util.Observableклас пропонує наступні засоби:

  1. Ведення списку java.util.Observerпримірників. Нові екземпляри, зацікавлені отримувати сповіщення, можна додавати addObserver(Observer o)та видаляти через deleteObserver(Observer o).

  2. Підтримка внутрішнього стану із зазначенням того, чи змінився об'єкт з моменту останнього повідомлення спостерігачам. Це корисно, оскільки воно відокремлює частину, де ви говорите, що Observableзміна змінилася, від тієї частини, де ви повідомляєте про зміни. (Наприклад, корисно, якщо у вас відбувається кілька змін, і ви хочете повідомити їх лише в кінці процесу, а не на кожному маленькому кроці). Це робиться наскрізь setChanged(). Таким чином, ви просто називаєте це, коли ви щось змінили на, Observableі ви хочете, щоб решта з Observersчасом дізналися про це.

  3. Повідомлення всіх спостерігачів про те, що конкретний Observableзмінився стан. Це робиться наскрізь notifyObservers(). Це перевіряє, чи об’єкт насправді змінився (тобто setChanged()був здійснений дзвінок ), перш ніж продовжувати повідомлення. Є дві версії, одна без аргументів і одна з 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

Шістнадцятковий рядок: F

Восьмі струни: 17

Бінарний рядок: 1111

Друга зміна стану: 10

Шістнадцятковий рядок: A

Восьмі струни: 12

Бінарний рядок: 1010


чудово пояснено :)
roottraveller

3
Я думаю, що «Визначення» - це друкарська помилка. Я сподіваюся, що це помилка друку.
JohnJohn

10

Вони є частинами структури дизайну спостерігача . Зазвичай один або більше спостерігачів отримують інформацію про зміни в одному спостережуваному . Це повідомлення про те, що "щось" сталося, де ви як програміст можете визначити, що означає "щось".

Використовуючи цю схему, ви роз'єднуєте обидві сутності один від одного - спостерігачі стають підключеними.


Я буду вдячний, якщо ви додасте пояснення board.changeMessage("More Homework!");у своїй відповіді, я маю на увазі, що станеться при посиланні changeMessage("More Homework!");.
Раві

9

Зворотний виклик спостерігача, зареєстрований у Observable.

Він використовується для інформування, наприклад, про події, що сталися в якийсь момент часу. Він широко використовується в Swing, Ajax, GWT для диспетчерських операцій, наприклад, подій UI (натискання кнопок, змінені текстові поля тощо).

У Swing ви знайдете такі методи, як addXXXListener (Listener l), у GWT у вас є (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

"Я намагався з'ясувати, навіщо саме нам потрібні спостерігач і спостережуване"

Як уже було сказано в попередніх відповідях, вони надають засоби передплати спостерігача для отримання автоматичного сповіщення спостережуваного.

Один із прикладів програми, де це може бути корисним, полягає у прив'язці даних , скажімо, у вас є якийсь інтерфейс, який редагує деякі дані, і ви хочете, щоб інтерфейс реагував, коли дані оновлюються, ви можете зробити свої дані видимими та підписати свої компоненти інтерфейсу на дані

Knockout.js - це сценарій JavaScript для MVVM, який має чудовий посібник для початку роботи, щоб побачити більше спостережуваних дій, які я дійсно рекомендую пройти підручник. http://learn.knockoutjs.com/

Я також знайшов цю статтю на стартовій сторінці Visual Studio 2008 ( Шаблон спостерігача - основа розробки контролера модельного перегляду (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

Фрагмент від публікації:

Шаблон спостерігача: По суті, він встановлює взаємозв'язок між багатьма об'єктами і має слабко пов'язаний дизайн між взаємозалежними об'єктами.

Визначення TextBook: Шаблон Observer визначає залежність від багатьох до об'єктів, так що коли один об'єкт змінює стан, усі його залежні сповіщаються та оновлюються автоматично.

Розгляньте, наприклад, службу сповіщення каналу Моделі передплати найкраще зрозуміти модель спостерігачів.


0

Шаблон спостерігача використовується, коли між об'єктами існує взаємозв'язок між багатьма, наприклад, якщо один об'єкт модифікований, його залежні об'єкти повинні повідомлятися автоматично.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.