Skip to content

Latest commit

 

History

History
128 lines (111 loc) · 6.42 KB

File metadata and controls

128 lines (111 loc) · 6.42 KB
title description keywords date
Наблюдатель
Обзор паттерна "Наблюдатель" и его реализация в JavaScript.
js, наблюдатель, паттерны
2018-09-22 04:00:00 -0700

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

Объекты желающие подписаться на событие называются наблюдателями. Они должны реализовывать общий интерфейс, который будет запущен в нужное время.

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

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

class MapListeners {
    constructor() {
        // в свойстве m будет храниться хэш карта вида:
        // {eventName: [object1, object2, ...], eventName2: [...], ...}
        this.m = {};
    }
    add(listener, event) {
        if (this.m.hasOwnProperty(event)) {
            this.m[event].push(listener);
        } else {
            this.m[event] = [];
            this.m[event].push(listener);
        }
    }
    remove(listener, event) {
        if (this.m.hasOwnProperty(event)) {
            this.m[event].forEach(l => {
                if (l === listener) {
                    this.m[event].splice(this.m[event].indexOf(listener), 1);
                    if (Object.keys(this.m[event]).length == 0) {
                        delete this.m[event];
                    }
                }
            });
        }
    }
    get() {
        return this.m;
    }
}

С помощью нашей новой структуры данных реализуем код издателя.

class Publisher {
    constructor() {
        this.mapList = new MapListeners();
    }
    subscribe(listener, event) {
        this.mapList.add(listener, event);
    }
    unscribe(listener, event) {
        this.mapList.remove(listener, event);
    }
    notify(event, data) {
        if (this.mapList.get()[event]) {
            this.mapList.get()[event].forEach(listener => {
                listener.update(data);
            });
        }
    }
}

Теперь с помощью данных классов и паттерна "Наблюдатель" реализуем задачу - вывод в консоль определенных событий. В программе будет выводиться последовательность чисел от 1 до 12, при этом в лог будут выводиться сообщения о том, что число положительное, отрицательное и больше ли оно пяти. В процессе выполнения программы мы динамически отпишем одно из событий. Создадим для этого несколько подписчиков. Подписчик должен обязательно реализовать метод update():

class OddLogger {
    update(data) {
        console.log(data + " нечетное число");
    }
}

class EvenLogger {
    update(data) {
        console.log(data + " четное число");
    }
}

class MoreThanFiveLogger {
    update(data) {
        console.log(data + " больше пяти");
    }
}

Осталось написать непосредственно код приложения:

// инициируем издателя
const publisher = new Publisher();

// инициируем подписчиков
const oddLogger = new OddLogger();
const evenLogger = new EvenLogger();
const moreThanFiveLogger = new MoreThanFiveLogger();

// подписываем подписчиков на события
publisher.subscribe(oddLogger, "odd");
publisher.subscribe(evenLogger, "even");
publisher.subscribe(moreThanFiveLogger, "moreThanFive");

// выведем числа от 1 до 12
// обновляя информацию для подписчиков
// после 10 удалим одну из подписок
for (let i = 1; i <= 12; i++) {
    if (i % 2 === 0 && i > 0) {
        publisher.notify("even", i);
    } else {
        publisher.notify("odd", i);
    }
    if (i > 10) {
        publisher.unscribe(moreThanFiveLogger, "moreThanFive");
    }
    if (i > 5) {
        publisher.notify("moreThanFive", i);
    }
}

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