Паттерн «наблюдатель»

Рассмотрим применение делегатов для обеспечения связи между объектами по типу «источник — наблюдатель». В результате разбиения системы на множество совместно работающих классов появляется необходимость поддерживать согла­сованное состояние взаимосвязанных объектов. При этом желательно избежать жесткой связанности классов, так как это часто негативно сказывается на воз­можности многократного использования кода.

Для обеспечения гибкой, динамической связи между объектами во время выпол­нения программы применяется следующая стратегия. Объект, называемый ис­точником, при изменении своего состояния, которое может представлять инте­рес для других объектов, посылает им уведомления. Эти объекты называются наблюдателями. Получив уведомление, наблюдатель опрашивает источник, что­бы синхронизировать с ним свое состояние.

Примером такой стратегии может служить связь объекта с различными его пред­ставлениями, например, связь электронной таблицы с созданными на ее основе Диаграммами.

Программисты часто используют одну и ту же схему организации и взаимодей­ствия объектов в разных контекстах. За такими схемами закрепилось название паттерны, или шаблоны проектирования. Описанная стратегия известна под на­званием паттерн «наблюдатель».

Наблюдатель (observer) определяет между объектами зависимость типа «один ко многим», так что при изменении состоянии одного объекта все зависящие от него объекты получают извещение и автоматически обновляются. Рассмотрим пример (листинг 10.2), в котором демонстрируется схема оповещения источни­ком трех наблюдателей. Гипотетическое изменение состояния объекта модели­руется сообщением «OOPS!». Один из методов в демонстрационных целях сде­лан статическим.

Листинг 10.2. Оповещение наблюдателей с помощью делегата

using System;

namespace ConsoleApplication1

{

public delegate void Del(object о); // объявление делегата

class Subj // класс-источник

{

Del dels; // объявление экземпляра делегата

public void Register(Del d) // регистрация делегата

{

dels += d;

}

public void OOPS() // что-то произошло

{

Console.WriteLine("OOPS!");

if (dels!= null) dels(this); // оповещение наблюдателей

}

}

class ObsA // класс-наблюдатель

{

public void Do(object о) // реакция на событие источника

{

Console.WriteLine("Вижу, что OOPS!");

}

}

class ObsB // класс-наблюдатель

{

public static void See(object о) // реакция на событие источника

{

Console.WriteLine("Я тоже вижу, что OOPS!");

}

}

class Class1

{

static void Main()

{

Subj s = new Subj(); // объект класса-источника

ObsA ol = new ObsA(); // объекты

ObsA o2 = new ObsA(); // класса-наблюдателя

s.Register(new Del(o1.Do)); // регистрация методов

s.Register(new Del(o2.Do)); // наблюдателей в источнике

s.Register(new Del(ObsB.See)); // С экземпляры делегата)

s.OOPSC(); // инициирование события

}

}

}

В источнике объявляется экземпляр делегата, в этот экземпляр заносятся мето­ды тех объектов, которые хотят получать уведомление об изменении состояния источника. Этот процесс называется регистрацией делегатов. При регистрации имя метода добавляется к списку. Обратите внимание: для статического метода указывается имя класса, а для обычного метода — имя объекта. При наступлении «часа X» все зарегистрированные методы поочередно вызываются через делегат. Результат работы программы:

OOPS!

Вижу, что OOPS!

Вижу, что OOPS!

Я тоже вижу, что OOPS!

Для обеспечения обратной связи между наблюдателем и источником делегат объ­явлен с параметром типа object, через который в вызываемый метод передается ссылка на вызывающий объект. Следовательно, в вызываемом методе можно по­лучать информацию о состоянии вызывающего объекта и посылать ему сообще­ния (то есть вызывать методы этого объекта).

Связь «источник — наблюдатель» устанавливается во время выполнения про­граммы для каждого объекта по отдельности. Если наблюдатель больше не хочет получать уведомления от источника, можно удалить соответствующий метод из списка делегата с помощью метода Remove или перегруженной операции вычита­ния, например:

public void UnRegister(Del d) // удаление делегата

{

dels -= d;

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: