Рубрики
Без рубрики

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

Шаблон проектирования наблюдателя является широко распространенным решением для систем, управляемых событиями. В этой статье мы реализуем нашу собственную разобщенную систему, управляемую событиями, на Java.

Автор оригинала: Victoria Seniuk.

Вступление

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

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

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

Поведенческие Шаблоны Проектирования

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

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

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

Шаблон проектирования наблюдателя – это способ проектирования подсистемы, которая позволяет многим объектам автоматически реагировать на изменения конкретного объекта, за которым “наблюдают”.

В нем рассматривается декомпозиция Наблюдаемого и Наблюдателя s – или издателя и подписчиков.

Для Наблюдаемого объекта мы используем термин Субъект . Объекты, которые подписаны на изменения Субъекта, называются Наблюдателями . Субъект и Наблюдатели, как правило, находятся в зависимости “один ко многим”.

Шаблон проектирования наблюдателя также известен как Подписчик события или Слушатель шаблон.

Примечание: Java имеет официальную реализацию шаблона проектирования наблюдателя и является основой JMS (службы сообщений Java). Обычно он используется для создания приложений, управляемых событиями, хотя официальная реализация на самом деле не очень распространена, и многие люди реализуют шаблон в соответствии со своими собственными сценариями использования.

Мотивация

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

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

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

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

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

Реализация

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

В этом случае наш магазин является объектом наблюдения, и наши клиенты наблюдают за ним. Давайте определим Субъект и Наблюдатель интерфейсы для реализации наших объектов:

public interface Subject {
    public void addSubscriber(Observer observer);
    public void removeSubscriber(Observer observer);
    public void notifySubscribers();
}

Интерфейс Тема довольно прост. Он предоставляет методы для добавления и удаления подписчиков/наблюдателей и уведомления их об изменениях.

Интерфейс Observer еще проще:

public interface Observer {
    public void update(String message);
}

Единственное, что действительно нужно Наблюдателю , – это знать, когда есть новости от их объекта. Их поведение, основанное на этом обновлении, будет отличаться в разных классах.

С нашими интерфейсами покончено, давайте реализуем интерфейс Subject через магазин:

public class Store implements Subject {
    private List customers = new ArrayList<>();

    @Override
    public void addSubscriber(Observer customer) {
        customers.add(customer);
    }
    @Override
    public void removeSubscriber(Observer customer) {
        customers.remove(customer);
    }
    @Override
    public void notifySubscribers() {
        System.out.println("A new item is on sale! Act fast before it sells out!");
        for(Observer customer: customers) {
            customer.update("Sale!");
        }
    }
}

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

Метод notifySubscribers() просто просматривает их список и отправляет им обновление.

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

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Давайте продолжим и внедрим эти два типа клиентов:

public class ShopaholicCustomer implements Observer {
    @Override
    public void update(String message) {
        processMessage(message);
    }
    private void processMessage(String message) {
        System.out.println("Shopaholic customer is interested in buying the product on sale!");
        // A complex psychologic response to a sale by a shopaholic
    }
}

public class PassiveCustomer implements Observer {
    @Override
    public void update(String message) {
        System.out.println("Passive customer made note of the sale.");
        // Passive customer does not react to the message too much
    }
}

И, наконец, давайте взглянем на шаблон дизайна наблюдателя в действии, активировав продажу в магазине, за которой наблюдают несколько клиентов:

public static void main(String[] args) {
    // Initialization
    Subject fashionChainStores = new ChainStores();
    Observer customer1 = new PassiveCustomer();
    Observer customer2 = new ShopaholicCustomer();
    Observer customer3 = new ShopaholicCustomer();

    // Adding two customers to the newsletter
    fashionChainStores.addSubscriber(customer1);
    fashionChainStores.addSubscriber(customer2);

    // Notifying customers (observers)
    fashionChainStores.notifySubscribers();

    // A customer has decided not to continue following the newsletter
    fashionChainStores.removeSubscriber(customer1);

    // customer2 told customer3 that a sale is going on
    fashionChainStores.addSubscriber(customer3);

    // Notifying the updated list of customers
    fashionChainStores.notifySubscribers();
}

И запуск этого фрагмента кода приведет к:

A new item is on sale! Act fast before it sells out!
Passive customer made note of the sale.
Shopaholic customer is interested in buying the product on sale!
A new item is on sale! Act fast before it sells out!
Shopaholic customer is interested in buying the product on sale!
Shopaholic customer is interested in buying the product on sale!

Изменение состояния магазина приводит к изменению состояния подписанных клиентов. Этот же принцип применим к пожарной сигнализации или ленте новостей. Как только кто-то публикует сообщение, все наблюдатели уведомляются и предпринимают некоторые действия в зависимости от их ответственности/заинтересованности.

Мы можем изменить список наблюдателей субъекта в любой момент. Кроме того, мы можем добавить любую реализацию интерфейса Observer . Это дает нам возможность создать надежную систему, управляемую событиями, которая отправляет обновления наблюдателям и обновляет всю систему на основе изменений в одном конкретном объекте.

плюсы и минусы

Шаблон проектирования наблюдателя является большим вкладом в поддержку принципа проектирования “Открыто/закрыто”. Это помогает нам создавать конструкции с высокой когезией, но слабой связью.

Другими словами, Наблюдатель и Субъект имеют строго определенную миссию. Субъект обновляет Наблюдателя некоторой информацией и не знает о реализации Наблюдателя. Эта характеристика придает нам гибкость.

Этот шаблон позволяет нам добавлять и удалять наблюдателей в любое время. Для этого вам не нужно изменять ни Субъекта, ни Наблюдателя.

Однако в шаблоне проектирования наблюдателя есть проблема.

Порядок уведомлений не находится под нашим контролем. Во время уведомления у подписчиков нет приоритета.

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

Однако важно понимать, что шаблон-это высокоуровневое описание конкретного решения. Когда мы применяем шаблон к двум разным приложениям, наш код будет отличаться. Мы можем отсортировать наших наблюдателей, и они получат уведомление в ожидаемом порядке. Это не является особенностью шаблона проектирования наблюдателя, но это то, что мы можем сделать.

Вывод

Когда вы знаете шаблоны проектирования, некоторые сложные проблемы можно свести к проверенным простым решениям.

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

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