Lecture
Publisher-subscriber (also known as pub/sub) — is a behavioral messaging design pattern in which senders of messages, called publishers, are not directly bound in program code to the subscribers. Instead, messages are divided into classes and contain no information about their subscribers, if any. Likewise, subscribers deal with one or more classes of messages, abstracted from the specific publishers.
The publisher-subscriber pattern is an extension of the observer pattern, to which a description of an event channel has been added, specifically intended for notifying about events .
The publisher-subscriber pattern, together with the closely related concept of a message queue, is part of the toolkit of the event-oriented middleware of a large system. Most messaging systems support both the publisher-subscriber model and message queues in their API. An example of such a system is the Java Message Service (JMS) .
This pattern provides greater scalability and a more dynamic network topology.
The Observer pattern proposes storing, inside the publisher object, a list of references to the subscriber objects, with the publisher not required to maintain the subscription list itself. It will provide methods with which subscribers can add or remove themselves from the list.

Subscribing to events.
Now for the interesting part. When an important event occurs in the publisher, it will iterate through the list of subscribers and notify them of it by calling a specific method on the subscriber objects.
The publisher does not care what class a given subscriber has, since they must all follow a common interface and have a single notification method.

Event notifications.
Having seen how smoothly everything works, you can extract a common interface describing the subscription and unsubscription methods for all publishers. After this, subscribers will be able to work with different types of publishers, and also receive notifications from them through the same method.

In the publisher-subscriber model, subscribers usually receive only a subset of all published messages. The process of selecting messages to receive and processing them is called filtering. There are two main forms of filtering: topic-based and content-based.
In a system based on topics, messages are published to «topics» or named logical channels. Subscribers in such systems will receive all messages published to the topics they subscribed to, and all subscribers subscribed to the same topic will receive the same messages. The publisher is responsible for defining the classes of messages that subscribers subscribe to.
In a system based on content, messages are delivered to subscribers only if the attributes or content of those messages are accepted by the subscriber. In this system, the subscriber is responsible for classifying messages.
Some systems are a hybrid of these two systems: the publisher sends messages to a topic, while subscribers register content-based subscriptions for one or more topics.
In many implementations of the publisher-subscriber pattern, the publisher sends messages to an intermediary, which may be a message broker or a bus. In this case, subscribers register their subscription with this broker, which performs the filtering. The broker, as a rule, handles the storage and forwarding of messages to route them from the publisher to the subscriber. In addition, the broker can assign priorities to messages in the message queue before routing them.
Subscribers can subscribe to specific messages at coding time, during application initialization, or at runtime. In systems with a graphical user interface, subscribers can subscribe manually through commands (such as clicking a button). Some frameworks and software use configuration files in XML or JSON format for subscriptions, and such files are read during initialization. Other software systems can add or remove subscriptions at runtime, for example database triggers or RSS.
Most real-time distributed systems based on the DDS standard do not use brokers. Instead, each publisher and subscriber share metadata about one another. The publisher and the subscriber cache this information locally and route messages based on this information.
The publisher-subscriber pattern was first publicly presented in 1987 by the Association for Computing Machinery (ACM) at the «Principles of Operating Systems» symposium of the SOSP '87 conference, in the paper «Exploiting Virtual Synchrony in Distributed Systems. 123—138» as part of the news subsystem of the Isis Toolkit.
When scaling an application that subscribes to a partition, you need to deal with competing consumers. Only one instance of the application should handle a message sent to the partition. Fortunately, you can use a system that handles this problem. When several instances of a service with the same application identifier subscribe to a partition, it delivers each message to only one of them.
When, after the state of one object changes, something needs to be done in others, but you don't know in advance exactly which objects should react.
This problem can arise when developing user interface libraries, when you need to give third-party classes the ability to react to button clicks.
The Observer pattern allows any object with a subscriber interface to register to receive notifications about events occurring in publisher objects.
When some objects need to observe others, but only in certain cases.
Publishers maintain dynamic lists. All observers can subscribe or unsubscribe from receiving notifications right at program runtime.
Split your functionality into two parts: an independent core and optional dependent parts. The independent core will become the publisher. The dependent parts will become the subscribers.
Create a subscriber interface. Usually it is enough to define a single notification method in it.
Create a publisher interface and describe in it the subscription management operations. Remember that the publisher should only work with the common subscriber interface.
You need to decide where to put the subscription-management code, since it is usually the same for all types of publishers. The most obvious way is to move this code into an intermediate abstract class, from which all publishers will inherit.
But if you are integrating the pattern into existing classes, creating a new base class may be difficult. In this case, you can place the subscription logic in a helper object and delegate this work to it from the publishers.
Create the concrete publisher classes. Implement them so that after each state change they send notifications to all their subscribers.
Implement the notification method in the concrete subscribers. Don't forget to provide parameters through which the publisher could send some data related to the event that occurred.
Another option is possible, where the subscriber, upon receiving the notification, itself takes the needed data from the publisher object. But in this case, you will be forced to bind the subscriber class to a specific publisher class.
The client should create the necessary number of subscriber objects and subscribe them to the publishers.
Chain of Responsibility, Command, Mediator, and Observer show various ways of having request senders work with their receivers:
The difference between Mediator and Observer is not always obvious. Most often they act as competitors, but sometimes they can work together.
The goal of Mediator is to remove the mutual dependencies between components of a system. Instead, they become dependent on the mediator itself. On the other hand, the goal of Observer is to provide dynamic one-way communication, in which some objects depend indirectly on others.
Implementing Mediator by means of Observer is fairly popular. In this case, the mediator object acts as the publisher, and all the other components become subscribers and can dynamically track events occurring in the mediator. In this case it is hard to see how the two patterns differ.
But Mediator also has other implementations, where individual components are rigidly bound to the mediator object. Such code is unlikely to resemble Observer, but it will still remain a Mediator.
Conversely, in the case of implementing a mediator by means of Observer, imagine a program in which every component of the system becomes a publisher. Components can subscribe to one another while still not being bound to concrete classes. The program will consist of an entire network of Observers, with no central Mediator object.
Comments