Новая версия данной статьи.
Наблюдатель(Observer) – это поведенческий шаблон проектирования. Он определяет зависимость типа «один ко многим» таким образом, что при изменении объекта, все зависящие от его получают сообщение об этом событии.
Задача: Задачей данного паттерна является выбор подходящего алгоритма, на основе обрабатываемых данных или типу клиента.
Простейшая схема работы паттерна:
Данный шаблон стоит применять в тех случаях, когда существует как минимум один объект, рассылающий сообщения, имеется не менее одного получателя сообщения и количество получателей может меняться в процессе работы приложения. Шаблон часто применяется в тех случаях, когда отправителя не интересует, что получатели сделают с полученными данными.
Рассмотрим следующую проблему: есть сервис, который поддерживает несколько платформ (Windows Phone, Android), необходимо реализовать PUSH уведомления на все подписанные устройства.
Создадим класс Messages:
public delegate void MessagesContentChangeEventHandler(object sender, String message);
public class Messages
{
public event MessagesContentChangeEventHandler ContentChanged;
private readonly Random _rand;
public Messages()
{
_rand = new Random();
}
private String GetMessage()
{
var messages = new string[] {"Сообщение первое", "Тестирование Push уведомлений", "Срочная новость", "Оповещение!!!"};
return messages[_rand.Next(messages.Count())];
}
public void MessageAvailable()
{
var message = GetMessage();
if (ContentChanged != null)
{
ContentChanged(this, message);
}
}
}
Здесь стоит обратить внимание на событие ContentChanged, именно на это событие будут подписываться телефоны, для получения Push уведомления. А получать они его будут, когда будет вызван метод MessageAvailable. В нем случайным образом получается следующее сообщение для рассылки, затем, если есть подписчик, то вызывается событие ContentChanged.
Теперь напишем интерфейс IObserver.
interface IObserver
{
void Display();
void Update(object sender, String message);
}
Возможно это не самый оптимальный тип интерфейса, но для данной задачи вполне хватит и такого. Теперь создадим класс для оповещения Windows Phone девайса.
class WindowsPhone:IObserver
{
private String _name;
private String _message;
public WindowsPhone(String name)
{
_name = name;
}
public void Display()
{
Console.WriteLine("WP device {0}. Message is received: {1}", _name,_message );
}
public void Update(object sender, String message)
{
_message = message;
Display();
}
}
Как можно увидеть, метод Update подходит для подписки на событие ContentChanged.При его вызове, пере запишется текущее сообщение и вызовется метод Display(); Который выведет его на экран. Класс для Android устройств будет аналогичным:
class AndroidPhone:IObserver
{
private readonly String _name;
private String _message;
public AndroidPhone(string name)
{
_name = name;
}
public void Display()
{
Console.WriteLine("Android device {0}. Message is received: {1}", _name, _message);
}
public void Update(object sender, string message)
{
_message = message;
Display();
}
}
Теперь осталось протестировать написанный код:
var wp1 = new WindowsPhone("Lumia 620");
var wp2 = new WindowsPhone("Lumia 1020");
var wp3 = new WindowsPhone("Lumia 930");
var android1 = new AndroidPhone("Nexus 4");
var android2 = new AndroidPhone("Nexus 5");
var android3 = new AndroidPhone("Galaxy 3");
var observer = new Messages();
//Подписываем на уведомления Windows Phone девайсы
observer.ContentChanged += wp1.Update;
observer.ContentChanged += wp2.Update;
observer.ContentChanged += wp3.Update;
//Подписываем на уведомления Android девайсы
observer.ContentChanged += android1.Update;
observer.ContentChanged += android2.Update;
observer.ContentChanged += android3.Update;
//Первое PUSH уведомление
observer.MessageAvailable();
//Удаляем из рассылки Lumia 620 и Nexus 4
observer.ContentChanged -= wp1.Update;
observer.ContentChanged -= android1.Update;
Console.WriteLine("\n---Были удалены некоторые девайсы из рассылки---");
//Второе PUSH уведомление
observer.MessageAvailable();
Console.WriteLine("\n---Третья рассылка---");
//Третья рассылка
observer.MessageAvailable();