Назад Вперед Зміст


OBSERVER/СПОСТЕРІГАЧ

Техніки ООП

В основу категоризації каталогу патернів лягли три найпростіші об’єктно-орієнтовані техніки. Це техніка використання об’єктів фабрик, які породжують об’єкти-продукти, техніка використання об’єкта фасаду та техніка диспетчеризації.

Диспетчеризація

Техніка використання диспетчеризації має дві форми: «Ланцюжок об’єктів» і «Видавець-Підписник». Ці техніки були покладені в основу поведінкових патернів.

Видавець-Підписник

При використанні техніки «Видавець-Підписник» — об’єкт видавець викликає метод на об’єкті-підписнику, а об’єкт-підписник після цього викликає метод на об’єкті-видавці. Таким чином об’єкт видавець повідомляє об’єкт-підписник про настання певної події.

OBSERVER/СПОСТЕРІГАЧ — ВИЗНАЧАЄ ЗАЛЕЖНІСТЬ «ОДИН ДО БАГАТЬОХ» МІЖ ОБ’ЄКТАМИ ТАК, ЩО ПРИ ЗМІНАХ В ОДНОМУ ОБ’ЄКТІ ВСІ ПОВ’ЯЗАНІ З НИМ ОБ’ЄКТИ ДІЗНАЮТЬСЯ ПРО ЦЮ ЗМІНУ І ОНОВЛЮЮТЬСЯ АВТОМАТИЧНО.

Ознаки застосування патерна Спостерігач (Observer)

Використовуйте патерн Спостерігач у наступних випадках:

  1. Коли абстракція має два аспекти, один з яких залежить від іншого.
  2. Інкапсуляція цих аспектів у різні об’єкти дозволяє змінювати і повторно використовувати їх незалежно.
  3. Коли при модифікації одного об’єкта потрібно змінити інші, але невідомо, скільки саме об’єктів потрібно змінити.
  4. Коли один об’єкт повинен сповіщати інших, не роблячи припущень, не володіючи інформацією про сповіщувані об’єкти. Іншими словами, ви не хочете, щоб об’єкти були тісно пов’язані між собою.

Формально патерн Спостерігач можна виразити наступною схемою UML:

Елементи шаблону

  1. IObservable: представляє спостережуваний об’єкт. Визначає три методи: AddObserver() (для додавання спостерігача), RemoveObserver() (видалення спостерігача) та NotifyObservers() (повідомлення спостерігачів).
  2. ConcreteObservable: конкретна реалізація інтерфейсу IObservable. Визначає колекцію об’єктів спостерігачів.
  3. IObserver: представляє спостерігача, який підписується на всі повідомлення спостережуваного об’єкта. Визначає метод Update(), який викликається спостережуваним об’єктом для повідомлення спостерігача.
  4. ConcreteObserver: конкретна реалізація інтерфейсу IObserver.

При цьому спостережуваному об’єкту не потрібно нічого знати про спостерігача, крім того, що він реалізує метод Update(). За допомогою відношення агрегації реалізується слабке зв’язування обох компонентів. Зміни в спостережуваному об’єкті не впливають на спостерігача і навпаки.

В певний момент спостерігач може припинити спостереження. І після цього обидва об’єкти — спостерігач і спостережуваний — можуть існувати в системі незалежно один від одного.

Приклад із перехрестям.

Постановка задачі:

Створити консольний додаток.

Припустимо, у нас є перехрестя, на якому висить світлофор, і є пішоходи та автомобілі, які стежать за інформацією від світлофора (колір змінюється з червоного на зелений), і залежно від отриманої інформації виконують певні дії:

Тут спостережуваний об’єкт представлений інтерфейсом IObservable, а спостерігач — інтерфейсом IObserver.

interface IObservable
{
    // Метод для реєстрації нового спостерігача
    void RegisterObserver(IObserver o);
    
    // Метод для видалення спостерігача зі списку
    void RemoveObserver(IObserver o);
    
    // Метод для повідомлення всіх зареєстрованих спостерігачів про зміну стану
    void NotifyObservers();
}
/// 
/// Інтерфейс спостерігача (Observer)
/// 
public interface IObserver
{
    /// 
    /// Метод оновлення стану спостерігача
    /// 
    /// Об'єкт, що містить дані для оновлення
    void Update(object ob);
}

Реалізацією інтерфейсу IObservable є клас TrafficLightSingleton, який символізує світлофор. У цьому класі визначити метод ChangeLight(int i), який імітує зміну світла. Після зміни світла відбувається повідомлення всіх спостерігачів (пішоходів та водіїв транспортних засобів).

/// 
/// Клас-одинак (Singleton) для управління світлофором з реалізацією патерну Спостерігач
/// 
public class TrafficLightSingleton : IObservable
{
    private TrafficLightInfo _trafficLightInfo; // Поточна інформація про стан світлофора
    private List _observers;         // Список підписаних спостерігачів
    private static TrafficLightSingleton _instance; // Єдиний екземпляр класу

    // Приватний конструктор (частина патерну Singleton)
    private TrafficLightSingleton() 
    { 
        _observers = new List();
    }

    /// 
    /// Метод для отримання єдиного екземпляру класу
    /// 
    public static TrafficLightSingleton GetInstance()
    { 
        if (_instance == null)
        {
            _instance = new TrafficLightSingleton();
        }
        return _instance;
    }

    /// 
    /// Реєстрація нового спостерігача
    /// 
    public void RegisterObserver(IObserver observer)
    { 
        _observers.Add(observer);
    }

    /// 
    /// Видалення спостерігача зі списку
    /// 
    public void RemoveObserver(IObserver observer)
    { 
        _observers.Remove(observer);
    }

    /// 
    /// Сповіщення всіх спостерігачів про зміни
    /// 
    public void NotifyObservers()
    { 
        foreach (IObserver observer in _observers)
        { 
            observer.Update(_trafficLightInfo); 
        }
    }

    /// 
    /// Зміна стану світлофора для пішоходів
    /// 
    public void ChangeLightForPedestrian()
    { 
        // Зміна кольорів консолі для наочності
        Console.ForegroundColor = ConsoleColor.Black;
        Console.BackgroundColor = ConsoleColor.White;
        
        _trafficLightInfo.ChangeLight(); // Оновлення стану світлофора
        NotifyObservers();               // Сповіщення підписаних спостерігачів
    }
}

Реалізаціями інтерфейсу IObserver є класи Driver (водій), Pedestrian (пішохід). При цьому метод Update() інтерфейсу IObserver приймає як параметр деякий об’єкт TrafficLightInfo, який зберігає інформацію про поточний колір світлофора.

namespace ObserverSvetophor
{
    /// 
    /// Клас, що містить інформацію про стан світлофора
    /// 
    public class TrafficLightInfo
    {
        private Random _random = new Random(); // Генератор випадкових чисел

        /// 
        /// Змінює стан світлофора випадковим чином
        /// 
        /// Рядок, що вказує на поточний колір ("red" або "green")
        public string ChangeLight()
        {
            double randomValue = _random.NextDouble();
            if (Math.Round(randomValue) == 0) 
            { 
                return RedLightIsOn(); // Увімкнути червоний
            }
            else 
            { 
                return GreenLightIsOn(); // Увімкнути зелений
            }
        }

        /// 
        /// Встановлює червоний колір світлофора
        /// 
        /// Рядок "red"
        public string RedLightIsOn()
        {
            Console.ForegroundColor = ConsoleColor.Blue;   // Колір тексту
            Console.BackgroundColor = ConsoleColor.Red;     // Колір фону (червоний)
            return "red";
        }

        /// 
        /// Встановлює зелений колір світлофора
        /// 
        /// Рядок "green"
        public string GreenLightIsOn()
        {
            Console.ForegroundColor = ConsoleColor.Black;   // Колір тексту
            Console.BackgroundColor = ConsoleColor.Green;   // Колір фону (зелений)
            return "green";
        }
    }
}

Клас Driver (водій).

/// 
/// Клас водія, який реалізує інтерфейс спостерігача (IObserver)
/// 
public class Driver : IObserver
{
    private string _number;         // Номер транспортного засобу
    private IObservable _trafficLight;  // Суб'єкт спостереження (світлофор)

    /// 
    /// Конструктор класу водія
    /// 
    /// Номер транспортного засобу
    /// Світлофор, за яким спостерігає водій
    public Driver(string vehicleNumber, IObservable trafficLight)
    {
        this._number = vehicleNumber;
        _trafficLight = trafficLight;
        _trafficLight.RegisterObserver(this); // Реєстрація спостерігача
    }

    /// 
    /// Метод оновлення стану (викликається при зміні світлофора)
    /// 
    /// Об'єкт із інформацією про стан світлофора
    public void Update(object ob)
    {
        Random random = new Random();
        TrafficLightInfo trafficInfo = (TrafficLightInfo)ob;
        int randomValue = random.Next(2); // Генерує 0 або 1
        
        if (trafficInfo.ChangeLight(randomValue) == "red")
            VehicleMoved();    // Транспорт рухається
        else
            VehicleStopped();  // Транспорт зупинився
    }

    /// 
    /// Транспортний засіб рухається (червоне світло)
    /// 
    private void VehicleMoved()
    {
        Console.WriteLine("{0} їде, пішоходи стоять.", this._number);
    }

    /// 
    /// Транспортний засіб зупинився (зелене світло)
    /// 
    private void VehicleStopped()
    {
        Console.WriteLine("{0} зупинився, пішоходи йдуть.", this._number);
    }
}

Клас Pedestrian (пішохід).

/// 
/// Клас пішохода, що реалізує інтерфейс спостерігача (IObserver)
/// 
public class Pedestrian : IObserver
{
    private string _name;          // Ім'я пішохода
    private IObservable _trafficLight; // Об'єкт світлофора, за яким спостерігаємо

    /// 
    /// Конструктор класу пішохода
    /// 
    /// Ім'я пішохода
    /// Об'єкт світлофора
    public Pedestrian(string name, IObservable trafficLight)
    {
        this._name = name;
        _trafficLight = trafficLight;
        _trafficLight.RegisterObserver(this); // Реєстрація як спостерігача
    }

    /// 
    /// Метод оновлення стану (викликається при зміні світлофора)
    /// 
    /// Об'єкт із інформацією про стан світлофора
    public void Update(object ob)
    {
        Random random = new Random();
        TrafficLightInfo trafficInfo = (TrafficLightInfo)ob;
        int randomValue = random.Next(2); // Генерує 0 або 1
        
        if (trafficInfo.ChangeLight(randomValue) == "red")
            PedestrianStopped();  // Пішоход зупиняється
        else
            PedestrianCrossed();  // Пішоход переходить дорогу
    }

    /// 
    /// Метод для зупинки спостереження за світлофором
    /// 
    public void StopTracking()
    {
        _trafficLight.RemoveObserver(this); // Видалення спостерігача
        _trafficLight = null;               // Очищення посилання
    }

    /// 
    /// Дія пішохода при зеленому світлі
    /// 
    public void PedestrianCrossed()
    {
        Console.WriteLine("{0} переходить дорогу.", this._name);
    }

    /// 
    /// Дія пішохода при червоному світлі
    /// 
    public void PedestrianStopped()
    {
        Console.WriteLine("{0} зупинився.", this._name);
    }
}

Моделюємо задачу

// Головний клас програми
class Program
{
    static void Main(string[] args)
    {
        // Отримуємо єдиний екземпляр світлофора (Singleton)
        TrafficLightSingleton trafficLight = TrafficLightSingleton.GetInstance();

        // Створюємо першого водія (автобус) та пішохода (Петров)
        Driver busDriver = new Driver("Автобус", trafficLight);
        Pedestrian petrov = new Pedestrian("Петров", trafficLight);
        
        // Імітуємо зміну світла світлофора
        trafficLight.ChangeLightForPedestrian();
        
        // Пішоход Петров припиняє спостерігати за світлофором
        petrov.StopTracking();

        // Знову імітуємо зміну світла
        trafficLight.ChangeLightForPedestrian();

        // Додаємо нові транспортні засоби та пішоходів
        Driver tramDriver = new Driver("Трамвай", trafficLight);
        Pedestrian ivanov = new Pedestrian("Іванов", trafficLight);
        trafficLight.ChangeLightForPedestrian();
        ivanov.StopTracking();

        Driver trolleybusDriver = new Driver("Тролейбус", trafficLight);
        Pedestrian sidorov = new Pedestrian("Сидоров", trafficLight);
        trafficLight.ChangeLightForPedestrian();
        sidorov.StopTracking();

        // Фінальна зміна світла
        trafficLight.ChangeLightForPedestrian();

        Console.ReadKey(); // Чекаємо натискання клавіші перед закриттям
    }
}

Ще одна реалізація симулятора світлофора:

Світлофор з Observer та Singleton

🚦 Світлофор з патернами Singleton і Observer

🚗 Статус транспорту: Чекати
🚶 Статус пішохода: Йти

🧠 Реалізація патерна Singleton + Observer (C#)

using System;

// Singleton: єдиний екземпляр світлофора
public sealed class TrafficLight
{
    private static readonly TrafficLight instance = new TrafficLight();
    private string currentColor = "Red"; // Початковий стан
    
    private TrafficLight() {} // Приватний конструктор
    public static TrafficLight Instance => instance;

    // Подія для Observer
    public event Action<string> OnColorChanged;
    
    public void ChangeColor(string newColor)
    {
        currentColor = newColor;
        OnColorChanged?.Invoke(newColor);
    }
    
    public string GetColor() => currentColor;
}
using  System;
// Observer: слухачі світлофора
public class Car
{
    public void React(string color)
    {
        Console.ForegroundColor = ConsoleColor.White;
        Console.Write("Автомобіль: ");
        
        switch (color)
        {
            case "Green":
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Їхати");
                break;
            case "Yellow":
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Готуватись до зупинки");
                break;
            case "Red":
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Зупинитись");
                break;
            default:
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("Чекати");
                break;
        }
    }
}
public class Pedestrian
{
    public void React(string color)
    {
        Console.ForegroundColor = ConsoleColor.White;
        Console.Write("Пішохід:   ");
        
        switch (color)
        {
            case "Green":
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Чекати");
                break;
            case "Yellow":
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Готуватись до переходу");
                break;
            case "Red":
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Можна переходити");
                break;
            default:
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("Очікувати");
                break;
        }
    }
}
class Program
{
    static void Main()
    {
        Console.OutputEncoding = System.Text.Encoding.UTF8;
        TrafficLight light = TrafficLight.Instance;
        
        light.OnColorChanged += new Car().React;
        light.OnColorChanged += new Pedestrian().React;
        
        string[] colors = { "Green", "Yellow", "Red" };
        
        foreach (var color in colors)
        {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine($"\nСвітлофор змінюється на {color}:");
            light.ChangeColor(color);
            
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine("Поточний колір: " + light.GetColor());
        }
        
        Console.ResetColor();
    }
}

Приклад виводу:

Світлофор змінюється на Green: Автомобіль: Їхати Пішохід: Чекати Поточний колір: Green Світлофор змінюється на Yellow: Автомобіль: Готуватись до зупинки Пішохід: Готуватись до переходу Поточний колір: Yellow Світлофор змінюється на Red: Автомобіль: Зупинитись Пішохід: Можна переходити Поточний колір: Red

Приклад із банком

Постановка задачі:

Створити консольний додаток.

Припустимо, у нас є біржа, де проходять торги, і є брокери та банки, які стежать за інформацією і в залежності від отриманої інформації виконують певні дії:

  1. Банк продає євро за курсом євро.
  2. Банк купує євро за курсом.

Тут спостережуваний об’єкт представлений інтерфейсом IObservable, а спостерігач — інтерфейсом IObserver.

// Інтерфейс IObservable визначає методи для роботи з спостерігачами (Observers)
interface IObservable
{
    // Метод для реєстрації нового спостерігача
    // Параметр: o - об'єкт, який реалізує інтерфейс IObserver
    void RegisterObserver(IObserver o);

    // Метод для видалення спостерігача зі списку
    // Параметр: o - об'єкт, який потрібно видалити
    void RemoveObserver(IObserver o);

    // Метод для сповіщення всіх зареєстрованих спостерігачів про зміни
    void NotifyObservers();
}
// Інтерфейс IObserver визначає метод для оновлення даних у спостерігача (Observer)
interface IObserver
{
    // Метод, який викликається при отриманні сповіщення від об'єкта, за яким ведеться спостереження (Observable)
    // Параметр: ob - об'єкт, що передає дані для оновлення (може бути будь-якого типу)
    void Update(Object ob);
}

Реалізацією інтерфейсу IObservable є клас Stock, який символізує валютну біржу. У цьому класі визначений метод Market(), який імітує торги і інкапсулює всю інформацію про валютні курси в об’єкті StockInfo. Після проведення торгів відбувається повідомлення всіх спостерігачів.

Реалізаціями інтерфейсу IObserver є класи Broker, який представляє брокера, і Bank, який представляє банк. Метод Update() інтерфейсу IObserver приймає як параметр певний об’єкт.

// Клас Bank, який реалізує інтерфейс IObserver для реагування на зміни курсу валют
class Bank : IObserver
{
    // Назва банку
    public string Name { get; set; }

    // Посилання на об'єкт, за яким ведеться спостереження (наприклад, біржа)
    private IObservable stock;

    // Конструктор класу Bank
    // Параметри:
    // - name: назва банку
    // - obs: об'єкт, який реалізує інтерфейс IObservable (наприклад, біржа)
    public Bank(string name, IObservable obs)
    {
        this.Name = name; // Ініціалізація назви банку
        stock = obs; // Збереження посилання на об'єкт спостереження
        stock.RegisterObserver(this); // Реєстрація банку як спостерігача
    }

    // Метод, який викликається при оновленні даних від об'єкта спостереження
    // Параметр:
    // - ob: об'єкт, що містить інформацію про зміни (у цьому випадку StockInfo)
    public void Update(object ob)
    {
        // Приведення типу об'єкта до StockInfo
        StockInfo sInfo = (StockInfo)ob;

        // Логіка реагування на зміни курсу євро
        if (sInfo.Euro > 40)
            Console.WriteLine("Банк {0} продає євро; Курс євро: {1}", this.Name, sInfo.Euro);
        else
            Console.WriteLine("Банк {0} купує євро; Курс євро: {1}", this.Name, sInfo.Euro);
    }
}
  • Клас Broker — реалізує інтерфейс IObserver, що дозволяє йому отримувати сповіщення про зміни курсу долара.
  • Властивість Name — зберігає ім'я брокера.
  • Поле stock — посилання на об'єкт, який сповіщає про зміни (наприклад, біржа).
  • Конструктор Broker:
    • Ініціалізує ім'я брокера.
    • Зберігає посилання на об'єкт спостереження (IObservable).
    • Реєструє брокера як спостерігача за допомогою RegisterObserver(this).
  • Метод Update:
    • Отримує дані про зміни курсу долара (StockInfo).
    • Виконує логіку: якщо курс долара вищий за 30, брокер продає долари, інакше — купує.
    • Виводить інформацію про дії брокера в консоль.
  • Метод StopTrade:
    • Видаляє брокера зі списку спостерігачів за допомогою RemoveObserver(this).
    • Очищує посилання на об'єкт спостереження (stock = null).
// Клас Broker, який реалізує інтерфейс IObserver для реагування на зміни курсу долара
class Broker : IObserver
{
    // Ім'я брокера
    public string Name { get; set; }

    // Посилання на об'єкт, за яким ведеться спостереження (наприклад, біржа)
    private IObservable stock;

    // Конструктор класу Broker
    // Параметри:
    // - name: ім'я брокера
    // - obs: об'єкт, який реалізує інтерфейс IObservable (наприклад, біржа)
    public Broker(string name, IObservable obs)
    {
        this.Name = name; // Ініціалізація імені брокера
        stock = obs; // Збереження посилання на об'єкт спостереження
        stock.RegisterObserver(this); // Реєстрація брокера як спостерігача
    }

    // Метод, який викликається при оновленні даних від об'єкта спостереження
    // Параметр:
    // - ob: об'єкт, що містить інформацію про зміни (у цьому випадку StockInfo)
    public void Update(object ob)
    {
        // Приведення типу об'єкта до StockInfo
        StockInfo sInfo = (StockInfo)ob;

        // Логіка реагування на зміни курсу долара
        if (sInfo.USD > 30)
            Console.WriteLine("Брокер {0} продає долари; Курс долара: {1}", this.Name, sInfo.USD);
        else
            Console.WriteLine("Брокер {0} купує долари; Курс долара: {1}", this.Name, sInfo.USD);
    }

    // Метод для припинення спостереження та видалення брокера зі списку спостерігачів
    public void StopTrade()
    {
        if (stock != null)
        {
            stock.RemoveObserver(this); // Видалення брокера зі списку спостерігачів
            stock = null; // Очищення посилання на об'єкт спостереження
        }
    }
}

Реалізація цього методу передбачає отримання через цей параметр об’єкта StockInfo з актуальною інформацією про торги і виконання деяких дій: купівля або продаж доларів і євро. Справа в тому, що часто потрібно інформувати спостерігача про зміну стану спостережуваного об’єкта.

У цьому випадку стан закладено в об’єкті StockInfo. Одним із варіантів інформування спостерігача про стан є push-модель, при якій спостережуваний об’єкт передає (іншими словами, «штовхає» — push) дані про свій стан, тобто передає їх у вигляді параметра методу Update().


// Клас Stock, який реалізує інтерфейс IObservable для керування спостерігачами (Observers)
class Stock : IObservable
{
    // Інформація про торги (курси валют)
    private StockInfo sInfo;

    // Список спостерігачів, які слідкують за змінами
    private List observers;

    // Конструктор класу Stock
    public Stock()
    {
        observers = new List(); // Ініціалізація списку спостерігачів
        sInfo = new StockInfo();          // Створення об'єкта для зберігання даних про торги
    }

    // Метод для реєстрації нового спостерігача
    // Параметр: o - об'єкт, який буде доданий до списку спостерігачів
    public void RegisterObserver(IObserver o)
    {
        observers.Add(o);
    }

    // Метод для видалення спостерігача зі списку
    // Параметр: o - об'єкт, який буде видалений зі списку спостерігачів
    public void RemoveObserver(IObserver o)
    {
        observers.Remove(o);
    }

    // Метод для сповіщення всіх спостерігачів про зміни
    public void NotifyObservers()
    {
        foreach (IObserver o in observers)
        {
            o.Update(sInfo); // Виклик методу Update у кожного спостерігача
        }
    }

    // Метод для імітації торгів на біржі
    public void Market()
    {
        Random rnd = new Random();
        sInfo.USD = rnd.Next(20, 40); // Генерація випадкового курсу долара (від 20 до 40)
        sInfo.Euro = rnd.Next(30, 50); // Генерація випадкового курсу євро (від 30 до 50)
        NotifyObservers(); // Сповіщення всіх спостерігачів про зміни
    }
}
  • Клас Stock — реалізує інтерфейс IObservable і відповідає за управління спостерігачами та сповіщення їх про зміни.
  • StockInfo sInfo — об'єкт, що містить інформацію про торги (курси валют).
  • List<IObserver> observers — список об'єктів, які слідкують за змінами.
    • Методи:
    • RegisterObserver — додає спостерігача до списку.
    • RemoveObserver — видаляє спостерігача зі списку.
    • NotifyObservers — сповіщає всіх спостерігачів про зміни, викликаючи їх метод Update.
    • Market — імітує торги, генеруючи випадкові значення курсів валют та сповіщаючи спостерігачів.

// Клас StockInfo, який містить інформацію про курси валют
class StockInfo
{
    // Властивість для зберігання курсу долара США
    // Значення: ціле число, що представляє курс
    public int USD { get; set; }

    // Властивість для зберігання курсу євро
    // Значення: ціле число, що представляє курс
    public int Euro { get; set; }
}

// Головний метод програми, який демонструє роботу паттерна Спостерігач (Observer)
static void Main(string[] args)
{
    // 1. Створення біржі (об'єкта, за яким будуть спостерігати)
    Stock stock = new Stock();

    // 2. Створення та підписка перших учасників ринку
    Bank bank = new Bank("Oщagбанк", stock);
    Broker broker = new Broker("Петров", stock);

    // 3. Перша імітація торгів - всі підписники отримують сповіщення
    stock.Market();

    // 4. Брокер Петров припиняє спостереження
    broker.StopTrade();

    // 5. Друга імітація торгів - тепер лише банк отримує сповіщення
    stock.Market();

    Console.WriteLine();  // Роздільник у виводі
    Console.WriteLine();

    // 6. Додавання нових учасників ринку
    Bank bank1 = new Bank("ПриватБанк", stock);
    Broker broker1 = new Broker("Коротков", stock);

    // 7. Третя імітація торгів - тепер сповіщення отримують:
    // - Ouagбанк (який залишався підписаним)
    // - ПриватБанк (новий)
    // - Коротков (новий брокер)
    stock.Market();

    // 8. Брокер Коротков припиняє спостереження
    broker1.StopTrade();

    // 9. Четверта імітація торгів - тепер сповіщення отримують:
    // - Ouagбанк
    // - ПриватБанк
    stock.Market();

    // Очікування натискання клавіші для завершення програми
    Console.ReadKey();
}

// 1. Створюємо біржу - об'єкт, за яким спостерігатимуть
Stock stock = new Stock();
Тут ми створюємо головний об'єкт (біржу), який буде містити інформацію про курси валют та сповіщати всіх підписаних учасників про зміни.
// 2. Додаємо перших учасників ринку
Bank bank = new Bank("Ouagбанк", stock);
Broker broker = new Broker("Петров", stock);

При створенні банку та брокера вони автоматично підписуються на сповіщення від біржі через конструктор.

// 3. Запускаємо торги - генеруємо курси валют
stock.Market();

Метод Market() виконує три дії:

  1. Генерує випадкові значення курсів долара (20-40) та євро (30-50)
  2. Сповіщає всіх підписаних учасників про зміни
  3. Кожен учасник приймає рішення - купувати чи продавати валюту
// 4. Брокер Петров припиняє спостереження
broker.StopTrade();

Метод StopTrade() виконує:

  • Видаляє брокера зі списку спостерігачів біржі
  • Відтепер цей брокер не отримуватиме сповіщення
// 5. Додаємо нових учасників
Bank bank1 = new Bank("ПриватБанк", stock);
Broker broker1 = new Broker("Коротков", stock);

Нові учасники автоматично підписуються на сповіщення при створенні.

Ключова особливість паттерна Спостерігач - динамічна зміна списку підписників. Учасники можуть підписуватись і відписуватись в будь-який момент, а біржа автоматично сповіщає лише поточних підписників.

Схема взаємодії:

    [Біржа Stock]
        │
        ├── [Bank "Oщagбанк"] (завжди підписаний)
        ├── [Bank "ПриватБанк"] (підписаний пізніше)
        └── [Broker "Коротков"] (тимчасово підписаний)
    

Програма завершується очікуванням натискання клавіші (Console.ReadKey()), що дає змогу побачити всі результати в консолі.


Назад Вперед Зміст