Статьи

Шаблоны проектирования: шаблон наблюдателя

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

Та же концепция применима и к шаблону дизайна. Мы должны реализовать этот шаблон, когда у нас есть зависимость «один ко многим» между нашим объектом, и один объект должен быть изменен / уведомлен, когда какие-либо изменения были внесены в любой другой объект.

Википедия говорит то же самое в словах ниже:

Шаблон наблюдателя — это шаблон разработки программного обеспечения, в котором объект, называемый субъектом, ведет список своих зависимых, называемых наблюдателями, и автоматически уведомляет их о любых изменениях состояния, обычно вызывая один из их методов. Он в основном используется для реализации распределенных систем обработки событий. Шаблон Observer также является ключевой частью в привычном архитектурном шаблоне модель-представление-контроллер (MVC).

Чтобы подробнее узнать об этом шаблоне проектирования, я беру пример симулятора, который показывает стоимость различных валют по отношению к доллару США. Мы предполагаем, что симулятор показывает цену, а также обновляет цену через равные промежутки времени.

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

  1. Этот наблюдатель должен иметь возможность добавлять новую валюту, чтобы клиент мог добавить любое количество валют по своему желанию.
  2. Этот наблюдатель должен сохранить ссылку на все добавленные валюты.
  3. Этот наблюдатель должен показать статус / значение каждой зарегистрированной валюты.

В следующем разделе мы реализуем нашего наблюдателя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
interface Observer {
    public function addCurrency(Currency $currency);
}
 
class priceSimulator implements Observer {
    private $currencies;
     
    public function __construct() {
        $this->currencies = array();
    }
     
    public function addCurrency(Currency $currency) {
        array_push($this->currencies, $currency);
    }
     
    public function updatePrice() {
        foreach ($this->currencies as $currency) {
            $currency->update();
        }
    }
}

Если вы посмотрите на приведенный выше код, вы увидите, что он способен выполнять все обязанности, которые мы упоминали в предыдущем разделе.

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

01
02
03
04
05
06
07
08
09
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
interface Currency {
    public function update();
    public function getPrice();
}
 
class Pound implements Currency {
    private $price;
     
    public function __construct($price) {
        $this->price = $price;
        echo «<p>Pound Original Price: {$price}</p>»;
    }
     
    public function update() {
        $this->price = $this->getPrice();
        echo «<p>Pound Updated Price : {$this->price}</p>»;
    }
     
    public function getPrice() {
        return f_rand(0.65, 0.71);
    }
     
}
 
class Yen implements Currency {
    private $price;
 
    public function __construct($price) {
        $this->price = $price;
        echo «<p>Yen Original Price : {$price}</p>»;
    }
 
    public function update() {
        $this->price = $this->getPrice();
        echo «<p>Yen Updated Price : {$this->price}</p>»;
    }
     
    public function getPrice() {
        return f_rand(120.52, 122.50);
    }
     
}

Мы все готовы собрать все вместе и запустить нашего наблюдателя.

01
02
03
04
05
06
07
08
09
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
interface Observer {
    public function addCurrency(Currency $currency);
}
 
class priceSimulator implements Observer {
    private $currencies;
     
    public function __construct() {
        $this->currencies = array();
    }
     
    public function addCurrency(Currency $currency) {
        array_push($this->currencies, $currency);
    }
     
    public function updatePrice() {
        foreach ($this->currencies as $currency) {
            $currency->update();
        }
    }
}
 
interface Currency {
    public function update();
    public function getPrice();
}
 
class Pound implements Currency {
    private $price;
     
    public function __construct($price) {
        $this->price = $price;
        echo «<p>Pound Original Price: {$price}</p>»;
    }
     
    public function update() {
        $this->price = $this->getPrice();
        echo «<p>Pound Updated Price : {$this->price}</p>»;
    }
     
    public function getPrice() {
        return f_rand(0.65, 0.71);
    }
     
}
 
class Yen implements Currency {
    private $price;
 
    public function __construct($price) {
        $this->price = $price;
        echo «<p>Yen Original Price : {$price}</p>»;
    }
 
    public function update() {
        $this->price = $this->getPrice();
        echo «<p>Yen Updated Price : {$this->price}</p>»;
    }
     
    public function getPrice() {
        return f_rand(120.52, 122.50);
    }
     
}
 
function f_rand($min=0,$max=1,$mul=1000000){
    if ($min>$max) return false;
    return mt_rand($min*$mul,$max*$mul)/$mul;
}
 
$priceSimulator = new priceSimulator();
 
$currency1 = new Pound(0.60);
$currency2 = new Yen(122);
 
$priceSimulator->addCurrency($currency1);
$priceSimulator->addCurrency($currency2);
 
echo «<hr />»;
$priceSimulator->updatePrice();
 
echo «<hr />»;
$priceSimulator->updatePrice();

Код выше выведет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Pound Original Price: 0.6
 
Yen Original Price : 122
 
————-
 
Pound Updated Price : 0.65346
 
Yen Updated Price : 121.287809
 
————-
 
Pound Updated Price : 0.671269
 
Yen Updated Price : 121.300605

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

Эта модификация включает в себя регистрацию валют только на симуляторе. Таким образом, наш клиентский код остается неизменным всякий раз, когда он вызывает обновление цены в нашем симуляторе.

Добавление новой валюты

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
class Euro implements Currency {
    private $price;
 
    public function __construct($price) {
        $this->price = $price;
        echo «<p>Euro Original Price : {$price}</p>»;
    }
 
    public function update() {
        $this->price = $this->getPrice();
        echo «<p>Euro Updated Price : {$this->price}</p>»;
    }
     
    public function getPrice() {
        return f_rand(0.78, 0.85);
    }
     
}

Добавить новую валюту было легко и просто. Теперь все, что нам нужно сделать, это зарегистрировать эту новую валюту в нашем наблюдателе, и мы все сделали!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
$priceSimulator = new priceSimulator();
 
$currency1 = new Pound(0.60);
$currency2 = new Yen(122);
$currency3 = new Euro(122);
 
$priceSimulator->addCurrency($currency1);
$priceSimulator->addCurrency($currency2);
$priceSimulator->addCurrency($currency3);
 
echo «<hr />»;
$priceSimulator->updatePrice();
 
echo «<hr />»;
$priceSimulator->updatePrice();

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

Не забудьте оставить свой отзыв ниже.