Code

Implementing the Observer Pattern in Java

Implementing the Observer Pattern in Java

Free course: “Quick start in Python»

Learn More

On February 18, 2021, as part of NASA's Mars 2020 mission, the Perseverance rover, known as Perseverance, successfully landed on the surface of Mars. Since its landing, it has been transmitting extensive data about the Red Planet back to Earth. The rover's research helps scientists better understand Mars's geology, atmosphere, and potential habitable conditions. The data collected by Perseverance plays a key role in preparing future missions to explore Mars and search for traces of ancient life on this mysterious planet.

You have been invited to work at NASA, and your first task is to study the data received from the Perseverance rover. This task requires careful analysis and a deep understanding of the information collected, which can unlock the mysteries of the Red Planet. Immerse yourself in the world of space exploration and discover the unique opportunities provided by data on the geology, atmosphere, and possible life on Mars. Your contribution to the analysis of this data can be an important step in further research and missions to explore other planets.

Contents

  • Problem Statement
  • Solution 1: Simple and Obvious
  • What is the Observer Pattern?
  • Solution 2: Flexible and Universal
  • How Else Can You Send Updates?
  • Solution 3: Standardized
  • To Sum Up

Problem Statement

The terms of reference are a document that clearly outlines the requirements and conditions for the project. It serves as the foundation for development, allowing all participants to share a common understanding of the goal, objectives, and expected results. It is important that the specifications include a description of the functional and non-functional requirements, as well as acceptance criteria. A well-written specification facilitates more effective interaction between the client and the contractor, minimizes risks, and accelerates the development process. Drafting such a document requires care and attention to detail, which ultimately impacts the success of the project. Creating a program that will effectively process new data received from the rover is an important task. The program must be able to distinguish between data types and take appropriate actions depending on their content. This may include data analysis, filtering, storing in a database, or sending for further processing. Effective data processing will allow for a better understanding of the information received from the rover and its use in making informed decisions. Proper organization of data management will improve the overall efficiency of processes and ensure a timely response to changes in information received from the rover.

  • The temperature on Mars will be displayed on a large screen in the hall;
  • The pressure on Mars will be shown on a screen in the laboratory;
  • The latest photographs of the surface will be published on the NASA website.

The list of available data processing methods is incomplete. It is necessary to ensure the ability to quickly integrate new processors and disable outdated solutions.

The accompanying documents contain up-to-date data from the rover. This is key information necessary for understanding the operation of the device and analyzing the collected data. Storing such documents provides access to important information and facilitates the process of using it in further research.

The data processing template class is the foundation on which the functionality for working with various types of information is built. Such a class can include methods for receiving, processing, and storing data, which allows for efficient code organization and reduction of its duplication. Using a template class simplifies the development and testing process, providing flexibility and scalability. This is especially important for projects that require working with large volumes of data or integrating with various information sources. Optimizing the code structure with such classes improves performance and simplifies further support and modification of the project.

The first solution came to mind almost instantly.

Solution one: simple and obvious

There is an effective method that is activated when new data is received. In this method, we can immediately distribute the received data among the necessary representations. First of all, it is necessary to describe the classes that will be responsible for these representations. This will organize the data structure and ensure their correct display.

Various methods and technologies can be used to display temperature. One of the most common methods is the use of thermometers, which can be both analog and digital. Analog thermometers display the temperature on a dial, while digital devices display the information on a screen, making them easier to read quickly.

Another option is to use software that can collect data from various temperature sensors. Such systems can be integrated into a smart home, allowing for automatic climate control. Mobile apps can also provide real-time temperature information using data from weather stations.

It's important to keep in mind that temperature measurement accuracy depends on the device chosen and the conditions in which it's used. Choosing the right temperature measurement method will provide reliable data for a variety of needs, whether it's indoor climate control, weather monitoring, or scientific research.

Various methods and instruments are used to measure pressure. It's important to select the right equipment depending on the application, whether it's industrial, medical, or scientific. Pressure can be measured in both absolute and relative units. The choice of pressure sensor type depends on operating conditions and measurement accuracy. Factors such as temperature, humidity, and the chemical resistance of materials should also be considered. Proper adjustment and calibration of equipment ensure high accuracy and reliability of the data obtained. Modern technologies allow pressure sensors to be integrated into automated systems, significantly simplifying process monitoring and control.

When publishing photographs, it's important to consider several key aspects. First, it's essential to select high-quality images that will attract attention. Second, optimize photographs for the web by reducing their size without losing quality to ensure fast page loading. It's also recommended to use appropriate keywords in image titles and attributes, which will help improve search engine visibility. Don't forget about properly formatted metadata, such as descriptions and tags, which also contributes to SEO. Finally, it's important to regularly update content and engage with your audience to maintain interest in your publications.

The simplest and most effective way to send new information is by sending notifications. Let's look at the key aspects that will help organize this process. First, you need to define your target audience to clearly understand who will receive updates. Then, you should choose the appropriate distribution channel, whether it's email, instant messaging, or social media.

Furthermore, it's important to create engaging and informative content for your newsletter that will engage recipients and encourage them to take action. Don't forget to keep your subscriber base current by periodically updating it and removing inactive contacts.

Use analytics tools to track the effectiveness of your newsletters and adjust your strategy. This will allow you to adapt to the needs of your audience and improve the quality of the information you provide. By considering all these aspects, you will be able to create an effective system for distributing new data that will benefit both your subscribers and your business.

The work is quick and simple, but there are some drawbacks.

  • Adding a new view will require changing the Perseverance class again.
  • It is not possible to disable views and add new ones directly at runtime.
  • Since Perseverance uses implementations - concrete view classes, rather than interfaces - then whenever a different implementation of any view appears, the rover class will again need to be changed.

The solution lacks flexibility, and the coupling between the rover class and the data presentation classes is too tight. However, there is a more interesting approach - the Observer design pattern is ideal for this problem. This pattern allows you to decouple classes, which makes the system more adaptive and easily changeable, and also improves dependency management. Using the Observer pattern will provide greater modularity and simplify the data updating process, which is especially important for complex systems such as Mars rovers.

What is the Observer Pattern?

In the classic book "Object-Oriented Design Patterns" written by the famous Gang of Four, this pattern is presented as follows:

A one-to-many relationship describes a connection between objects in which a change in the state of one object automatically results in all dependent objects being notified of the change. This ensures data synchronization and allows all related elements to be updated in real time. This approach is especially useful in systems where it is important to keep information up-to-date and minimize the likelihood of errors associated with data desynchronization.

In real life, there are many examples of the effective application of this pattern. This tool finds its application in various fields, demonstrating its versatility and usefulness. The template helps structure information, simplifies the analysis process, and facilitates faster decision-making. Its use helps optimize work processes and increase productivity. As a result, many companies and organizations have already appreciated the benefits this template provides, implementing it in their practices to achieve better results.

  • subscription to channels, communities, and friends' news on social networks;
  • subscription to receive information about the release of new episodes of your favorite series in online cinemas;
  • subscription to notifications about price changes on a product you like in an online store.
Examples of the "Observer" pattern in real life. Image: Maya Malgina for Skillbox Media

The key element is "subscription". Without a subscription, the entire flow of information turns into ordinary spam. Subscription allows you to receive relevant and interesting materials that match your interests. This ensures more targeted and useful content, getting rid of unnecessary information. Subscription creates a connection between the sender and the recipient, allowing you to receive important updates and news in a convenient format. It makes the interaction more personalized, increasing the value of the information for the reader.

The "Observer" pattern distinguishes two main types of participants: sources of updates and subscribers who receive these updates. To start receiving updates, you must first register on the subscriber list. If a subscriber decides to unsubscribe, they will no longer receive updates. This pattern is widely used in programming and software design, allowing for efficient management of interactions between components and ensuring that information is up-to-date for users.

Participants in this design pattern are typically divided into two types: Subject and Observer. Both of these types represent interfaces that serve as the basis for creating custom implementation classes. These classes can efficiently store the current state of the Subject and Observers, enabling change notification mechanisms. Using such interfaces facilitates code maintenance and extensibility, allowing developers to create more flexible and scalable systems.

The Subject has methods for subscribing, unsubscribing, and notifying all of its subscribers. The Observer, in turn, has a method that is invoked when new data is received from the Subject. These mechanisms ensure effective interaction between system components, allowing information to be kept up-to-date and changes to be responded to promptly.

Class diagram. Infographics: Ekaterina Stepanova / Skillbox Media

Solution Two: Flexible and Universal

Let's start with interface development. The first interface will be for the Subject, which will include the key attributes and methods necessary for its functionality. Creating a clearly defined interface for the Subject will ensure structure and ease of further use and implementation into the system. The interface should take into account the main characteristics and actions that the Subject can perform, which will allow for the effective management of its behavior and interaction with other system components.

The second option is for Observers. This section focuses on providing information and tools that will help Observers effectively perform their functions. The presence of Observers plays a key role in ensuring the transparency and objectivity of processes. It is important that Observers have access to up-to-date data and recommendations, allowing them to make informed decisions. Observer support and training help improve their skills and, as a result, the quality of observations.

Now we will adapt the implementation of the Perseverance class to conform to the Subject interface. This will improve the code structure and simplify interaction with observers, ensuring better manageability and extensibility of functionality. Implementing the Subject interface will allow observers to be added, removed, and notified of the state of the Perseverance object, making it more flexible and convenient for use in various scenarios.

Perseverance stores the list of Observers in the observers variable. A Set data structure is used, which eliminates the possibility of duplicating representations of the same type. In this case, the order in which Observers are notified is irrelevant. This ensures efficient management of observers and ensures that each of them is notified only once when the state changes.

When new data is received, the notifyObservers method is called, which activates the update method for each subscribed Observer. This process ensures that all interested parties are updated in real time, ensuring that they receive up-to-date information. Understanding how these methods work is key to developing effective systems that use the Observer pattern.

The TemperatureDisplay, PressureDisplay, and PhotoPublisher classes will be updated. These changes are aimed at improving the functionality and performance of the systems responsible for displaying temperature, pressure, and publishing photos. Optimizing these classes will improve data accuracy and user experience, which in turn will positively impact the overall performance of the application. The updates will provide a more intuitive interface and enhanced capabilities for end users.

  • Let's point out that each of them now implements the Observer interface.
  • Let's create a constructor with a Subject parameter and register as an Observer directly when creating the class.

TemperatureDisplay is a component designed to display temperature in a convenient and understandable format. It can be used in various applications where visualization of temperature data is required. The main features of TemperatureDisplay include displaying the current temperature, the ability to select units of measurement (Celsius or Fahrenheit), and a responsive design for various devices. This component is ideal for weather apps, smart home systems, and other climate control services. Using TemperatureDisplay allows users to quickly obtain up-to-date information on temperature conditions, making it an indispensable tool for everyday use.

Let's create a test case to ensure that the program functions as expected. Testing the functionality of the application is important to ensure its reliability and efficiency. Testing will help identify potential errors and improve the performance of the program.

We have created instances of three view classes, so the first call to the onNewData method requires all three classes to receive updates and process them according to their update method implementations. This approach ensures synchronous data updates in different views, which is important for maintaining information consistency in the application.

We remove the temperature display from the list of subscribers, so the second call to update data should not result in a new temperature value being displayed. This change optimizes the update process and prevents unnecessary temperature notifications, which improves the user experience.

Let's run the application and confirm that it works correctly.

Console output after running the program. Screenshot: Ekaterina Stepanova / Skillbox Media

New data from the rover can be passed to the update method in various ways. One approach is to pass the entire Subject instance to the update method. In this case, the getData method can be used to retrieve the latest data. This approach allows for flexible data management and simplifies the update process, providing the ability to work with the full Subject data.

In this case, we convert the Subject instance to PerseveranceData format to access temperature, pressure, and photo data. This allows for efficient extraction of the necessary parameters and analysis of information related to surface conditions.

This approach makes it possible to apply the Observer interface to a variety of tasks, not limited to rovers. The update method is now independent of the data format used by the rover. The specific data being passed is known only to the interface implementations, providing greater flexibility and extensibility.

Solution Three: Standardized

Instead of creating your own Subject and Observer interfaces, you can use ready-made solutions in Java. The java.beans package provides the PropertyChangeSupport class and the PropertyChangeListener interface, which are ideal for implementing the Observer pattern. These tools make it easy to track changes in object properties and notify stakeholders of these changes, which simplifies development and improves code readability. Using standard libraries not only speeds up the development process but also ensures the reliability and compatibility of your application.

For correct operation, you must add an instance of PropertyChangeSupport to the Subject class. Observer classes must implement the PropertyChangeListener interface. This will provide a mechanism for notifying Observers of changes in the Subject's state, which is an important aspect of implementing the Observer pattern. Properly setting up this communication will improve event management and increase the efficiency of interactions between system components.

The new version of Perseverance will be an advanced model with enhanced performance and functionality. This rover will continue its exploration of the surface of the Red Planet, collecting data on geology, climate, and potential signs of life. The updated version of Perseverance is equipped with more powerful tools for analyzing samples and conducting experiments, which will allow for more accurate and detailed results. An improved navigation system will ensure greater autonomy and efficiency in exploration missions. Perseverance will be an important step in the exploration of Mars and preparation for future manned flights.

The temperature display class is a convenient tool for working with temperature data. It allows you to easily and efficiently display temperature information in various units of measurement, such as Celsius, Fahrenheit, and Kelvin.

This class can be used in web applications, meteorological services, and any other projects that require temperature display. It provides easy integration and high performance, making it an ideal choice for developers.

With this class, users can not only view the current temperature but also convert values ​​between different scales. This makes it a versatile tool for working with climate and weather data.

The PropertyChangeSupport class provides functionality for managing observers in Java. It implements methods for adding and removing observers, as well as the firePropertyChange method, which is responsible for notifying all registered observers of changes. This method accepts three parameters: the change type, the previous data, and the new data. Using the PropertyChangeSupport class, developers can effectively implement the Observer design pattern, which allows for dynamic data updates in applications when the state of objects changes.

This mechanism provides Observers with advanced capabilities. They can be configured to respond to certain categories of events or perform actions only when a specified level of change is reached. For example, the temperature can be displayed only if it changes by 5 degrees or more. This improves system efficiency and minimizes unnecessary notifications.

Java provides the java.util.Observer interface and the java.util.Observable class, which are used to implement the Observer pattern. To do this, you can inherit the Subject class from Observable and implement the Observer interface in your Observer classes. This approach allows you to efficiently organize interactions between objects, ensuring that the state of Observers is automatically updated when the state of the Subject changes. Using these components simplifies managing dependencies between objects and promotes cleaner code architecture.

Since Java 9, the Observer and Observable classes have been marked as deprecated, meaning their use is not recommended. To implement the observer pattern, it is recommended to use the PropertyChangeSupport and PropertyChangeListener classes. These alternatives offer a more modern approach to managing property changes and provide more flexible and secure event handling in Java applications.

To summarize

The Observer design pattern is an effective tool for implementing a mechanism for notifying objects about changes in one object to another. It is especially useful in situations where it is necessary to organize interaction between objects through subscriptions. Using this pattern allows for the creation of a flexible and easily extensible architecture in which observer objects can receive notifications about the state of publisher objects. This ensures a high degree of modularity and simplifies the process of introducing changes to the system, since new subscribers can be added without requiring changes to the code of existing components. The Observer pattern is widely used in the development of web applications, monitoring systems, and other areas where data synchronization is required.

  • New Observer subscribers are added without changes to existing classes;
  • The implementations of the Subject and Observers are separated from each other, so the business logic in the Observers can be changed as needed without editing the Subject.

Learn various design patterns, algorithms, data structures, and object-oriented programming concepts with practical examples in Java in the "Java Developer PRO" course. Master one of the most in-demand programming languages, learn to develop high-quality applications for various platforms, and also get assistance in finding a job from Skillbox.