Добро пожаловать на авторский блог Авдеева Марка.

Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%

<<< Хостинг для интернет магазина || jQuery добавить атрибут >>>

Паттерн Observer

20.11.2012
Паттерн Observer

Здравствуйте, уважаемые читатели блога LifeExample, в этой статье я расскажу вам об еще одном полезном паттерне программирования. Ранее я уже писал о таких паттернах программирования как синглтон, и модель-вид-контролер, сегодня же хочу разобрать паттерн observer, он же паттерн наблюдатель.

Напомню, для тех, кто забыл, паттерном программирования можно считать заранее спроектированную конструкцию из взаимодействующих алгоритмов, которую можно применять для решения часто встречающихся задач.

Паттерн observer принадлежит разряду “шаблонов поведения”, к которому также относятся такие паттерны как:

  • Command (Команда)
  • Strategy (Стратегия)
  • Chain of Responsibility (Цепочка обязанностей)
  • Memento (Хранитель)
  • State (Состояние)
  • Interpreter (Интерпретатор)
  • Iterator (Итератор)
  • Mediator (Посредник)
  • Memento (Хранитель)
  • State (Состояние)
  • Template Method (Шаблонный метод)
  • Visitor (Посетитель)

Надеюсь их время еще придет, и я напишу о каждом отдельно.

Данный шаблон предназначен для установки неявной связи между классами и уведомлении оного или нескольких классов о случившихся изменениях в другом классе.

Смысл в том, что если какое-то действие происходит в одном классе, то оповещаются все классы, заинтересованные в данном изменении, и по своему его обрабатывают.

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

Спустя некоторое время я все-таки нашел применение паттерну observer в механизме плагинов, для известной вам CMS, которую, мы вместе начинали писать в статьях "Пишем интернет магазин на php ". О механизме плагинов я готовлю отдельную интересную статью, не забудьте подписаться на обновления блога.

Пример реализации паттерна observer

Если честно, мне иногда становится необычайно скучно перебирать тонны программного кода, когда необходимо вникнуть во что-то новое. Поэтому я хочу представить вашему вниманию реализацию паттерна observer, в отвлеченном, можно даже сказать, игровом виде.

Чтобы понять суть рассматриваемого паттерна, предлагаю смоделировать такую задачу.

Дано:

  1. Дон Аль Капоне;
  2. Банда грабителей банков;
  3. Новый член банды;
  4. Агенты Скали и Малдер;
  5. Штаб ФБР.

Задача:

Смоделировать действие преступной группировки, внедрить в нее нового члена банды и двух агентов ФБР. Банда должна выбрать и установить дату следующего ограбления, а агенты, как только овладеют информацией передать ее в штаб ФБР.

Решение

Сначала приведу, код, затем опишу, что там происходит.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?php

// Обработчик событий для классов
interface eventHandler{

  function reaction($obj);
}

// Клас описывающий сущность "Грабители банка"
class GangstersGroup{

  static private $instance = null;
  private $gangsters = array(); //члены банды
  private $dateHeist; //Дата следующего ограбления

  private function __construct(){

  }

  private function __clone(){

  }

  // получить единственный екземпляр этого класса
  static public function getInstance(){
    if(self::$instance == null){
      self::$instance = new GangstersGroup();
    }
    return self::$instance;
  }

  // получить дату ограбления
  public function getDateHeist(){
    return $this->dateHeist;
  }

  // Установить дату ограбления
  public function setDateHeist($date){
    $this->dateHeist = $date;

    //Сообщить всем членам банды информацию
    $this->notifyGangsters();
  }

  // Посвящение в члены банды
  public function introduceToGangstersGroup($gangsters){
    $this->gangsters[] = $gangsters;
  }

  //Сообщить всем  членам банды
  function notifyGangsters(){
    foreach($this->gangsters as $gangster){

      $gangster->reaction($this);
    }
  }

}

// Агент под прикрытием FBI
class Agent implements eventHandler{

  private $name = ""; //Имя агента

  public function __construct($name){

    // Агент получает кодовое имя
    $this->name = $name;

    // Внедряется в банду
    GangstersGroup::getInstance()->introduceToGangstersGroup($this);
  }

  // Докладывает в штаб ФБР
  public function reaction($obj){
    if($obj instanceof GangstersGroup){
      // Обновить информацию.
      print "<br/>...<br/>На связи агент ".$this->name.": известна дата ограбления! ".$obj->getDateHeist();
    }
  }

}

// Грабитель
class Robber implements eventHandler{

  private $name = "";

  public function __construct($name){

    // Грабитель получает имя
    $this->name = $name;

    // внедряется в банду
    GangstersGroup::getInstance()->introduceToGangstersGroup($this);
  }

  // Доложить
  public function reaction($obj){
    if($obj instanceof GangstersGroup){
      // Обновить информацию.
      print "<br/>Внимание всем членам банды! <strong>".$obj->getDateHeist()."</strong> Мы идем на <u>ограбление</u>! ";
    }
  }

}

//Создаем классы интересующиеся деятельностью GangstersGroup
$robber = new Robber('Bob');
$scully = new Agent('Scully');
$malder = new Agent('Malder');

// Шло время, выполнялись другие  функции...
// И вот Дон Аль Капоне сообщил дату ограбления:
GangstersGroup::getInstance()->setDateHeist(date("d-m-Y 00:00"));

В выше представленном программном коде, классом GangstersGroup описывается сущность (преступная группировка бандитов) внутри которой с течением времени могут происходить изменения (в нашем случае группировка должна выбрать дату ограбления).

Класс Robber описывает заинтересованный в событиях класса GangstersGroup , грабителя, который узнав, о на меченой дате, прокричит полученную информацию во все услышанные.

Класс Agent передает сущность, доблестных, агентов Скали и Малдера, не побоявшихся внедриться в ряды преступной организации на ряду с обычными грабителями Robber. Данный класс реагирует на полученную информацию передачей ее в штаб квартиру ФБР, рапортуя свое имя и дату ограбления.

В общем в описанном пояснении и заключается принцип паттерна observer: определенные экземпляры классов Agent и Robber, получают возможность обработать событие произошедшие в классе GangstersGroup. Приведенный выше код, демонстрирует это выводом на экран следующих сообщений:

пример паттерн observer

Не знаю как вам, а мне такой пример нравится больше, нежели классический пример реализующий паттерн observer из wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
interface Observer{
        function notify($obj);
}
 
class ExchangeRate{
        static private $instance = NULL;
        private $observers = array();
        private $exchange_rate;
 
        private function __construct(){}
        private function __clone(){}
 
        static public function getInstance(){
                if(self::$instance == NULL){
                        self::$instance = new ExchangeRate();
                }
                return self::$instance;
        }
 
        public function getExchangeRate(){
                return $this->exchange_rate;
        }
 
        public function setExchangeRate($new_rate){
                $this->exchange_rate = $new_rate;
                $this->notifyObservers();
        }
 
        public function registerObserver(Observer $obj){
                $this->observers[] = $obj;
        }
 
        function notifyObservers(){
                foreach($this->observers as $obj){
                        $obj->notify($this);
                }
        }
}
 
class ProductItem implements Observer{
 
        public function __construct(){
                ExchangeRate::getInstance()->registerObserver($this);
        }
 
        public function notify($obj){
                if($obj instanceof ExchangeRate) {
                        // Update exchange rate data
                        print "Received update!\n";
                }
        }
}
 
$product1 = new ProductItem();
$product2 = new ProductItem();
 
ExchangeRate::getInstance()->setExchangeRate(4.5);

Хотя, несомненно, взять его за каркас для своего приложение, буде явно удобнее. Если вам понравился пример реализации паттерна observer, или остались вопросы, а может быть есть замечания, то пишите обо всем этом в комментариях к статье и не забывайте подписаться на обновление, впереди будут публикации очень интересного материала.

Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.

Нравится

Комментарии

  • Андрей

    Отличный пример!!!!

  • Сережа

    Здравствуйте, а подскажите ссылку на тему «Пишем интернет магазин на php», в данной теме упоминается, но ссылка устарела.

  • Оставить комментарий

    Подписаться на комментарии к этой статье по RSS

    Размещение статей и контекстных ссылок
    Бесплатная CMS для вашего магазина
    Яндекс.Метрика