
Паттерн наблюдатель
Вы знакомы с понятием подписка. Сколько людей мучаются от бесконечных SMS, посылаемых различными магазинами, банками и т.д. о проходящих у них акциях, распродажах, кредитах. Эта ситуация описывается паттерном наблюдатель.
У нас есть наблюдаемый объект – магазин и множество наблюдателей за этим магазином – владельцы мобильных телефонов. Как только в магазине возникает какое-то неординарное событие (распродажа, акция, скидки), владельцы телефонов получают сообщение об этом событии.
Создадим два интерфейса Observer и Observable. Метод update интерфейса Observer предназначен для передачи сообщения клиентам. Методы registerObserver и removeObserver интерфейса Observable предназначены для регистрации и удаления клиентов магазина. Метод notifyObservers служит для уведомления клиентов о различных событиях в магазине.
package observer;
public interface Observer {
public void update (String sms);
}
package observer;
public interface Observable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
Класс Shop (магазин) наследует интерфейс Observable(доступный для наблюдения) и содержит список всех клиентов магазина (observers), дату очередной акции магазина (date) и посылаемое клиенту сообщение (message). Метод sendMessage создает сообщение, приписывая к сообщению текущую дату. При каждом изменении переменной message вызывается метод notifyObservers, который рассылает сообщение всем подписчикам, т.е. клиентам, зарегистрированным методом registerObserver.
package observer;
import java.util.ArrayList;
import java.util.Date;
public class Shop implements Observable{
Date date;
String message;
private ArrayList<Observer> observers = new ArrayList<Observer>();
void setDate(){
date = new Date();
}
String sendMessage(){
setDate();
return message+date;
}
void setMessage(String sms){
message= sms;
notifyObservers();
}
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 (Observer observer : observers){
observer.update(sendMessage());
}
}
}
Класс клиент Client наследует интерфейс Observer и содержит список всех сообщений, полученных клиентом (sms). В методе update очередное сообщение добавляется в список полученных sms. В методе disply выводятся все сообщения присланные клиенту.
package observer;
import java.util.ArrayList;
public class Client implements Observer {
ArrayList<String> sms;
String name;
Client(String name){
sms=new ArrayList<String>();
this.name = name;
}
public void update(String str) {
sms.add(str);
}
void disply(){
for(String x:sms)
System.out.println("Client "+name+" receive the message "+x);
}
}
Теперь необходимо создать магазин и его клиентов. Каждый клиент регистрируется в магазине - magazine.registerObserver(client[i]). Затем всем клиентам рассылаются сообщения с разной датой magazine.setMessage("Sale "). Демонстрируем все полученные клиентами сообщения
for(int i=0;i<4;i++){
client[i].disply();
}
package observer;
public static void main(String[] args) throws InterruptedException {
Shop magazine = new Shop();
Client [] client = new Client[4];
for(int i=0;i<4;i++){
client[i] = new Client("Cl "+i);
magazine.registerObserver(client[i]);
}
for(int i=0;i<5;i++){
Thread.sleep(1000);
magazine.setMessage("Sale ");
}
for(int i=0;i<4;i++){
client[i].disply();
}
}
}
В java.util имеется реализация наблюдателя и наблюдаемого объекта – интерфейс java.util .observer и класс java.util.observable. Интерфейс содержит метод update, а наблюдаемый объект может добавлять /удалять наблюдателей методами addObserver/deleteObserver. Также наблюдаемый объект сообщает наблюдателям о совершенных изменениях методом notifyObservers. Перед вызовом этого метода необходимо сообщить о том, что наблюдаемые объекты изменились - setChanged. Метод setChanged полезен в некоторых случаях. Например, изменение наблюдаемых параметров может происходить очень часто - раз в 10 милисекунд, а отражать это изменение нужно раз в секунду, тогда вызовом метода setChanged эта проблема решается.
Рассмотрим простой пример реализации наблюдателя с использованием java.util.
Пусть у нас имеется наблюдаемый объект TestObservable. Этот объект содержит только од ну переменную – строку name. Метод modify утверждает, что объект TestObservable изменился. Понятно, что данный объект изменяется только при создании.
public class TestObservable extends java.util.Observable {
private String name = "";
public TestObservable(String name) {
this.name = name;
}
public void modify() {
setChanged();
}
public String getName() {
return name;
}
}
Наблюдатель – экземпляр класса TestObserver, наследующий интерфейс java.util.Observable, перегружает метод update. В методе update используется объект типа Observable и объект типа Object. Вызвавший update объект «о» предоставляет метод getName для доступа к наблюдаемой строке name. Также в функции update доступен объект, переданный в качестве параметра функции notifyObservers объекта типа Observable . В данном случае таким параметром является строка.
public class TestObserver implements java.util.Observer {
private String name = "";
public TestObserver(String name) {
this.name = name;
}
public void update(java.util.Observable o,Object arg) {
String str = "Called update of " + name;
str += " from " + ((TestObservable)o).getName();
str += " with argument " + (String)arg;
System.out.println(str);
}
}
Рассмотрим реализацию паттерна наблюдатель. В функции main создаются объекты:
to – наблюдаемый объект,
о1 - наблюдатель Observer 1,
о2 - наблюдатель Observer 2.
Вызов метода addObserver для объекта to to.addObserver(o1) и to.addObserver(o2) назначает наблюдателей объекта to. Вызов метода modify, устанавливает состояние – «наблюдаемый объект был изменен». После этого notifyObservers сообщает наблюдателям об изменении наблюдаемого объекта.
public class Test {
public Test() {
}
public static void main(String[] args) {
Test test = new Test();
TestObservable to = new TestObservable("Observable");
TestObserver o1 = new TestObserver("Observer 1");
TestObserver o2 = new TestObserver("Observer 2");
to.addObserver(o1);
to.addObserver(o2);
to.modify();
to.notifyObservers("Notify argument");
}
}
В качестве упражнения напишите пример с рассылкой сообщений с использованием java.util.observer и класс java.util.observable.