{"id":24352605,"url":"https://github.com/flowduino/espressio-observable","last_synced_at":"2026-05-28T08:31:10.094Z","repository":{"id":224051780,"uuid":"752186245","full_name":"Flowduino/ESPressio-Observable","owner":"Flowduino","description":"Observable Components of the ESPressio Development Platform","archived":false,"fork":false,"pushed_at":"2024-02-23T14:43:03.000Z","size":81,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-19T17:26:29.487Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Flowduino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["Flowduino"]}},"created_at":"2024-02-03T09:32:55.000Z","updated_at":"2024-02-23T13:10:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"3518607a-ddd6-4f97-b0bd-94f7e0498331","html_url":"https://github.com/Flowduino/ESPressio-Observable","commit_stats":null,"previous_names":["flowduino/espressio-observable"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Flowduino/ESPressio-Observable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flowduino%2FESPressio-Observable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flowduino%2FESPressio-Observable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flowduino%2FESPressio-Observable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flowduino%2FESPressio-Observable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Flowduino","download_url":"https://codeload.github.com/Flowduino/ESPressio-Observable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flowduino%2FESPressio-Observable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33601380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-18T15:55:12.228Z","updated_at":"2026-05-28T08:31:10.078Z","avatar_url":"https://github.com/Flowduino.png","language":"C++","funding_links":["https://github.com/sponsors/Flowduino"],"categories":[],"sub_categories":[],"readme":"# ESPressio Observable\nObserver Pattern Components of the Flowduino ESPressio Development Platform\n\nProvides a foundation for designing, structuring, and implementing your embedded programs using Observer Pattern.\n\n## Latest Stable Version\nThe latest Stable Version is [1.0.1](https://github.com/Flowduino/ESPressio-Observable/releases/tag/1.0.1).\n\n## ESPressio Development Platform\nThe **ESPressio** Development Platform is a collection of discrete (sometimes intra-connected) Component Libraries developed with a particular development ethos in mind.\n\nThe key objectives of the ESPressio Development Platform are:\n- **Light-weight** - The Components should always strive to optimize memory consumption and operational overhead as much as possible, but not to the detriment of...\n- **Ease of Use** - Many of our components serve as Developer-Friendly Abstractions of existing procedural code libraries.\n- **Object-Oriented** - A `type` for everything, and everything in a `type`!\n- **SOLID**:\n- -  \u003e **S**ingle Responsibility Principle (SRP)\n    Break your code into smaller, focused components.\n- - \u003e **O**pen/Closed Principle (OCP)\n    Be open for extension but closed for modification.\n- - \u003e **L**iskov Substitution Principle (LSP)\n    Be substitutable for the base type without altering correctness.\n- - \u003e **I**nterface Segregation Principle (ISP)\n    Break interfaces into specific, client-focused ones.\n- - \u003e **D**ependency Inversion Principle (DIP)\n    Be dependent on abstractions, not concretions.\n\nTo the maximum extent possible within the limitations/restrictons/constraints of the C++ langauge, the Arduino platform, and Microcontroller Programming itself, all Component Libraries of the **ESPressio** Development Platform must strive to honour the **SOLID** principles.\n\n## License\nESPressio (and its component libraries, including this one) are subject to the *Apache License 2.0*\nPlease see the [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) accompanying this library for full details.\n\n## Namespace\nEvery type/variable/constant/etc. related to *ESPressio* Observable are located within the `Observable` sub-namespace of the `ESPressio` parent namespace.\n\nThe namespace provides the following (*click on any declaration to navigate to more info*):\n- [`ESPressio::Observable::IObserverHandle`](#iobserverhandle)\n- [`ESPressio::Observable::IObservable`](#iobservable)\n- [`ESPressio::Observable::IObserver`](#iobserver)\n- [`ESPressio::Observable::ObserverHandle`](#observerhandle)\n- [`ESPressio::Observable::ThreadSafeObservable`](#threadsafeobservable)\n\n## Platformio.ini\nYou can quickly and easily add this library to your project in PlatformIO by simply including the following in your `platformio.ini` file:\n\n```ini\nlib_deps =\n    flowduino/ESPressio-Observable@^1.0.1\n```\n\nAlternatively, if you want to use the bleeding-edge (effectively \"Developer Integration Testing\" or \"DIT\") sources, you can instead use:\n\n```ini\nlib_deps = \n    https://github.com/Flowduino/ESPressio-Observable.git\n```\nPlease note that this will use the very latest commits pushed into the repository, so volatility is possible.\n\n## RTTI is required for this library!\nThis library leverages fundamnetal C++ language features that in turn necessitate the use of RTTI (**R**un**T**ime **T**ype **I**nformation).\n\nIf you are developing with the Arduino framework but with the ESPressif platform, as of Febraury 22nd 2024, you may need to modify your Platformio.ini configuration as shown below to use a newer (pre-release) version of the packages where RTTI does not break any functionality when using `#include \u003cFS.h\u003e` in your code:\n```ini\nplatform = https://github.com/platformio/platform-espressif32.git\nplatform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git\n```\n\u003eNote that we have been informed that the next major release of the platform will resolve this issue, eliminating this requirement. However, as of February 22nd 2024, the above lines included in your Platformio.ini file is required.\n\nAdditionally, you should always define the following in your Platformio.ini file's build configurations:\n```ini\nbuild_unflags =\n\t-fno-rtti\n```\nWhere the above explicitly enables RTTI in your build configuration.\n\n## Understanding Observer Pattern\nObserver Pattern is a way of enabling any number of objects to be notified of specific operations or state changes occuring in another object.\n\nWhereas *callbacks* are inherently limited to a single observer being notified of an operation or state change, Observer Pattern (particularly as implemented in this library) enables *any number* of Observers to be notified of the same.\n\nBetter still, this library provides Interface-Mapped Observer behaviour, meaning that you can define any number of Interfaces containing notification callback method declarations grouped however makes the most sense for your use-case.\n\nIn practice, Observer Pattern facilitates \"one-way dependency\" even when two objects need to communicate bi-directionally. This eliminates the unwanted complexity of \"Circular Referencing\", and improves the abstraction of code.\n\n\u003e Observer Pattern is one step removed from *total decoupling*, which can be achieved using our [ESPressio Event](https://github.com/Flowduino/ESPressio-Event) library: a fully-functional solution for true \"Event-Driven Observer Pattern\".\n\n### The Do's and Don'ts of Observer Pattern:\n#### The Do's:\n* Always define logically-distinct Interfaces for Observers (demonstrated below in *Basic Usage*)\n* Always ensure that your object references are one-way (I.E. `Observer` references `Observable` but `Observable` never references `Observer`)\n* Try to keep `Observer` notification methods as short as reasonably possible, especially when working with multiple Threads. A notification should typically just update one or more state values.\n\n#### The Don'ts:\n* Never allow for circular references (`Observer` must know of `Observable`, but `Observable` must never know of `Observer`)\n* Never modify any object or value passed into an `Observer` notification method! The value should remain unchanged for *all* `Observer`s receiving the same notification, therefore none should ever modify any state or value being passed in an `Observer` notification.\n\n## Basic Usage\nESPressio Observable has been designed with ease of use in mind.\n\nWhat we shall do now is define a simple use-case, and show how to implement it using the ESPressio Observable library.\n\nFor our use-case, let's presume that we have a class called `Thermometer`, whose sole purpose is to check for changes in temperature and notify any observers when a change occurs.\n\n### The `Observable` Implementation...\n\u003e We shall presume taht the following code is in a header file called `Thermometer.hpp`\n```cpp\n#include \u003cESPressio_Observable\u003e\n\nusing namespace ESPressio::Observable\n\nclass Thermometer : public Observable::Observable {\n    private:\n        int _temperature;\n\n        void NotifyObservers(int oldTemperature, int newTemperature) {\n            // Here is where we shall implement our Observer Notification\n        }\n    public:\n        void UpdateTemperature() {\n            // Code to read the sensor data will go here (and obviously depends on the sensor being used and method of reading the data)\n            // Let's presume that the outcome is that we have an `int` variable called `temperature`\n            if (_temperature == temperature) { return; } // If the temperature hasn't changed, there's nothing to do!\n            // Now we know that the temperature has changed\n            NotifyObservers(_temperature, newTemperature); // Notify the Observers of the change...\n            _temperature = temperature; // ... then store the new Temperature value.\n        }\n\n        int GetTemperature() { return _temperature; }\n};\n```\n\nThe above implementation does not presume what kind of temperature sensor you are using, as that is not relevant to what we are covering in this usage example.\n\nYou will notice that there is a function named `NotifyObservers` that will be invoked every time the temperature changes.\n\nIn order for us to implement that function, we must first define an `Observer` type, which defines the *Interface* for our Observers.\n\n\u003e We shall presume that the following code is in a header file called `ITemperatureObserver.hpp`\n\n```cpp\nclass ITemperatureObserver {\n    public:\n        virtual void OnTemperatureChanged(int oldTemperature, int newTemperature) {}\n        virtual void OnTemperatureIncreased(int increasedBy) {}\n        virtual void OnTemperatureDecreased(int decreasedBy) {}\n};\n```\n\nThe above defines an *Interface* describing the methods available for any Observer of `Thermometer`.\n\nPay special attention to the fact that each of these `virtual` methods has a stubbed (empty) implementation. This means that you can choose which of the avialable Observer Methods to implement on each respective Observer.\nIf you were to make them Abstract (e.g. `virtual void OnTemperatureChanged(int oldTemperature, int newTemperature) = 0;`) then you would be *forced* to provide an explicit implementation for that method on each Observer type.\n\nBefore we implement `NotifyObservers` we need to add the additional `include` for our file containing the `ITemperatureObserver` interface:\n```cpp\n#include \u003cESPressio_Observable.hpp\u003e\n#include \"ITemperatureObserver.hpp\" // \u003c We add this line\n\nusing namespace ESPressio::Observable\n```\n\nNow we can implement the `NotifyObservers` method accordingly:\n```cpp\n        void NotifyObservers(int oldTemperature, int newTemperature) {\n            WithObservers\u003cITemperatureObserver\u003e([oldTemperature, newTemperature](ITemperatureObserver* observer) {\n                observer-\u003eOnTemperatureChanged(oldTemperature, newTemperature);\n                if (newTemperature \u003e oldTemperature) { // Temperature has Increased\n                    observer-\u003eOnTemperatureIncreased(newTemperature - oldTemperature);\n                }\n                else if (oldTemperature \u003e newTemperature) { // Temperature has Decreased\n                    observer-\u003eOnTemperatureDecreased(oldTemperature - newTemperature);\n                }\n            });\n        }\n```\nThe above implementation is performing a number of distinct processes.\n* It iterates through all registered *Observers* implementing the `ITemperatureObserver` interface\n  * For each of them...\n    * it will invoke the `OnTemperatureChanged` method, passing both the previous Temperature and the new Temperature values.\n    * If the temperature has *Increased*, it will invoke `OnTemperatureIncreased` with the temperature difference (delta) as its parameter value.\n    * If the temperature has *Decreased*, it will invoke `OnTempeeratureDecreased` with the temperature difference (delta) as its parameter value.\n\nSo, from the *Observable* side of this exchange, the implementation is now complete.\n\n### The `Observer` implementation...\nNow that we have the `Observable` implemented, or - more specifically - given that the `ITemperatureObserver` interface has been defined, we can now implement the `Observer` side of this exchange.\n\n\u003e Remember: The `Observable` does not care whether any other objects are Observing it. An `Observable` will operate exactly the same way no matter what.\n\nNow, let's define an `Observer` in a new header file called `TemperatureLogger.hpp` to do something very simple:\n```cpp\n#include \u003cESPressio_IObserver.hpp\u003e\n#include \"ITemperatureObserver.hpp\" // \u003c Remember to include the file containing our interface!\n\nusing namespace ESPressio::Observable\n\nclass TemperatureLogger : public IObserver, public ITemperatureObserver {\n    public:\n        void OnTemperatureChanged(int oldTemperature, int newTemperature) override {\n            Serial.printf(\"Temperature Changed from %d to %d.\\n\", oldTemperature, newTemperature);\n        }\n\n        void OnTemperatureIncreased(int increasedBy) override {\n            Serial.printf(\"Temperature Increased by %d.\\n\", increasedBy);\n        }\n\n        void OnTemperatureDecreased(int decreasedBy) override {\n            Serial.printf(\"Temperature Decreased by %d.\\n\", decreasedBy);\n        }\n};\n```\nThe above implementation of `TemperatureLoger` simply prints lines to the Serial console each time the corresponding Notification Method is invoked.\n\n\u003e Your `Observer`s can of course perform any behaviour applicable to your use-cases, this example is intentionally simple.\n\nSo, now we have our `Observable` and we have a simple `Observer` type, we need to register our `Observer` with our `Observable` to complete the exchange.\n\nSo, our `ino` or `main.cpp` file will look something like this:\n\n```cpp\n#include \u003cESPressio_IObserver.hpp\u003e\n#include \"TemperatureLogger.hpp\"\n#include \"Thermometer.hpp\"\n\nThermometer thermometer;\nTemperatureLogger temperatureLogger;\nIObserverHandle* observerHandle;\n\nvoid setup() {\n    observerHandle = thermometer.RegisterObserver(temperatureLogger); // Register our Observer with the Observable\n}\n\nvoid loop() {\n    thermometer.UpdateTemperature(); // We will update the temperature reading on the loop\n}\n```\nIt really is as simple as that!\n\n`RegisterObserver` registers the given `Observer` with the `Observable` against which it is invoked.\nIt returns an `IObserverHandle*` (pointer) reference which you should retain for the lifetime of your `Observer`. \n\nInvoking `delete observerHandle` will not only destroy the Observer Handle (freeing its memory), it will also unregister the `Observer` from the `Observable`.\nIn the case of the above (simple) example, both the `Observer` and the `Observable` exist for the entire lifetime of execution, however - you can fully control the lifetimes in your applications.\n\n### Remember: You can have as many `Observer`s as you want for a single `Observable`!\nAs this section title says, you can register as many `Observer`s as you require for any `Observable` type.\n\n### Thread-Safe `Observable`s\nESPressio Observable provides both a non-Thread-Safe and a Thread-Safe implementation.\n\nThe above example uses the non-Thread-Safe `Observable` implementation, but we can very easily substitute this with our `ThreadSafeObservable` counterpart:\n\n\u003e Modifying `Thermometer.hpp`\n\nFirst, let's replace:\n```cpp\n    #include \u003cESPressio_Observable.hpp\u003e\n```\nwith:\n```cpp\n    #include \u003cESPressio_ThreadSafeObservable.hpp\u003e\n```\n\nNow we simply modify the `Thermometer` class declaration from:\n```cpp\nclass Thermometer : public Observable::Observable {\n```\nto:\n```cpp\nclass Thermometer : public ThreadSafeObservable {\n```\nThere is no need to modify any further implementation, as both `Observable` and `ThreadSafeObservable` identically satisfy the `IObservable` interface. Only their internal implementations differ (the latter appropriately employing thread-safe locks to ensure safe and predictable behaviour when operating across multiple Threads).\n\n\u003e**CRITICAL** Just because you're using `ThreadSafeObservable` instead of `Observable`, that does not mean that custom members of **your** `Observable` type (e.g. `_temperature` in `Thermometer`) are implicitly thread-safe!\n\n\u003eIt is your responsibility to ensure that each member of your `Observable` type is appropriately encapsulated by the most suitable thread-safe locking mechanism for your use-case.\n\nFor broader Thread-Safe implementation needs, you should check out our [ESPressio Threads](https://github.com/Flowduino/ESPressio-Threads) library, which provides a simple and elegant means of making members of your own classes thread-safe.\n\n### One `Observable`, multiple `Observer` types\nThe previous examples have all illustrated how to implement a singular `Observer` type (`ITemperatureObserver`) for a singular `Observable` type (`Thermometer`).\n\nHowever, ESPressio Observable's clever implementation enables you to define and consume any number of `Observer` types for a singular `Observable` type.\n\nThis functionality is extremely useful, as it enables us to group related `Observer` notifications into discrete *Interfaces*, strengthing our adherence to the SOLID principles of software development; yielding cleaner, more maintainable code.\n\nTo expand on our previous example with the `Thermometer` `Observable` type, let us presume that our physical sensor provides Air Pressure in addition to Temperature. With that in mind, let's extend and modify our previous implementation to enable individual `Observer`s to be notified of each respective sensor value change.\n\nWe shall begin by adding a new file called `IAirPressureObserver.hpp`:\n```cpp\nclass IAirPressureObserver {\n    public:\n        virtual void OnAirPressureChanged(int oldPressure, int newPressure) {}\n        virtual void OnAirPressureIncreased(int increasedBy) {}\n        virtual void OnAirPressureDecreased(int decreasedBy) {}\n};\n```\nThe above code simply defines a new interface specific for Air Pressure events, and entirely separate from our previous Temperature events interface.\n\nNow we need to modify the `Observable` (`Thermometer`) to notify any `Observer` implementing `IAirPressureObserver`, so let's modify our `Thermometer.hpp` file accordingly:\n```cpp\n#include \u003cESPressio_Observable\u003e\n#include \"ITemperatureObserver.hpp\"\n#include \"IAirPressureObserver.hpp\" // \u003c We add the include for the new Interface\n\nusing namespace ESPressio::Observable\n\nclass Thermometer : public Observable::Observable {\n    private:\n        int _temperature;\n        int _airPressure;\n\n        // We will rename `NotifyObservers` to `NotifyTemperatureObservers` to avoid ambiguity\n        void NotifyTemperatureObservers(int oldTemperature, int newTemperature) {\n            WithObservers\u003cITemperatureObserver\u003e([oldTemperature, newTemperature](ITemperatureObserver* observer) {\n                observer-\u003eOnTemperatureChanged(oldTemperature, newTemperature);\n                if (newTemperature \u003e oldTemperature) { // Temperature has Increased\n                    observer-\u003eOnTemperatureIncreased(newTemperature - oldTemperature);\n                }\n                else if (oldTemperature \u003e newTemperature) { // Temperature has Decreased\n                    observer-\u003eOnTemperatureDecreased(oldTemperature - newTemperature);\n                }\n            });\n        }\n\n        // We will add `NotifyAirPressureObservers` per our expansion\n        void NotifyAirPressureObservers(int oldPressure, int newPressure) {\n            WithObservers\u003cIAirPressureObserver\u003e([oldPressure, newPressure](IAirPressureObserver* observer) {\n                observer-\u003eOnAirPressureChanged(oldPressure, newPressure);\n                if (newPressure \u003e oldPressure) { // Air Pressure has Increased\n                    observer-\u003eOnAirPressureIncreased(newPressure - oldPressure);\n                }\n                else if (oldPressure \u003e newPressure) { // Air Pressure has Decreased\n                    observer-\u003eOnAirPressureDecreased(oldPressure - newPressure);\n                }\n            });\n        }\n    public:\n        void UpdateTemperature() {\n            // Code to read the sensor data will go here (and obviously depends on the sensor being used and method of reading the data)\n            // Let's presume that the outcome is that we have an `int` variable called `temperature`\n            if (_temperature == temperature) { return; } // If the temperature hasn't changed, there's nothing to do!\n            // Now we know that the temperature has changed\n            NotifyTemperatureObservers(_temperature, newTemperature); // Notify the Observers of the change...\n            _temperature = temperature; // ... then store the new Temperature value.\n        }\n\n        void UpdateAirPressure() {\n            // Code to read the Air Pressure data will go here (different depending on sensor type being used)\n            // Let's presume that the outcome is that we have an `int` variable called `airPressure`\n            if (_airPressure == airPressure) { return; } // If the temperature hasn't changed, there's nothing to do!\n            // Now we know that the temperature has changed\n            NotifyAirPressureObservers(_airPressure, airPressure); // Notify the Observers of the change...\n            _airPressure = airPressure; // ... then store the new Temperature value.\n        }\n\n        int GetTemperature() { return _temperature; }\n        int GetAirPressure() { return _airPressure; }\n};\n```\nAbove is the completed expansion of our `Thermometer.hpp` header file, expanding on Temperature with the new Air Pressure implementation.\n\nThe important thing to note here is that we define which *Interface* to use in the call to `WithObservers`, and the implementation behind the scenes will ensure that the given *Lambda Function* is only ever invoked where an `Observer` satisfies (implements) that given *Interface* type.\n\nThis is a crucial feature of ESPressio Observable, and one which sets it apart from more rudimentary (and common) Obsever Pattern implementations, which are typically inflexible and enforce a singular (fixed) `Observer` interface.\n\nSo, with the `Observable` (`Thermometer`) now expanded to accept both `ITemperatureObserver` and `IAirPressureObserver` implementing `Observer`s, we can introduce a new `Observer` type specifically for `IAirPressureObserver`...\n\nNow, let's define a new `Observer` in a new header file called `AirPressureLogger.hpp` to do something very simple:\n```cpp\n#include \u003cESPressio_IObserver.hpp\u003e\n#include \"IAirPressureObserver.hpp\" // \u003c Remember to include the file containing our interface!\n\nusing namespace ESPressio::Observable\n\nclass AirPressureLogger : public IObserver, public IAirPressureObserver {\n    public:\n        void OnAirPressureChanged(int oldPressure, int newPressure) override {\n            Serial.printf(\"Air Pressure Changed from %d to %d.\\n\", oldPressure, newPressure);\n        }\n\n        void OnAirPressureIncreased(int increasedBy) override {\n            Serial.printf(\"Air Pressure Increased by %d.\\n\", increasedBy);\n        }\n\n        void OnAirPressureDecreased(int decreasedBy) override {\n            Serial.printf(\"Air Pressure Decreased by %d.\\n\", decreasedBy);\n        }\n};\n```\n\nJust like that, we have another (very simple) `Observer` type specifically interested in Air Pressure specific notifications, which will simply output these changes into the Serial console (just as we did with the `TemperatureLogger` implementation prevously)\n\nAgain, remember that your `Observer` implementation can perform whatever operations are relevant to your use-case for each Notificiation Function Implementation. This example is intentionally simplified.\n\nOkay, we're almost done... now we need only modify our `.ino` or `main.cpp` implementation to instantiate and register our new `Observer` (`AirPressureLogger`) with our `Observable` (`Thermometer`):\n```cpp\n#include \u003cESPressio_IObserver.hpp\u003e\n#include \"TemperatureLogger.hpp\"\n#include \"AirPressureLogger.hpp\" // We add the new Include for our new Air Pressure Logger class\n#include \"Thermometer.hpp\"\n\nThermometer thermometer;\nTemperatureLogger temperatureLogger;\nAirPressureLogger airPressureLogger;\nIObserverHandle* temperatureObserverHandle; // We shall rename `observerHandle` to `temperatureObserverHandle` to avoid ambiguity.\nIObserverHandle* airPressureObserverHandle; // We add a new variable to hold the Air Pressure Observer's Handle.\n\nvoid setup() {\n    temperatureObserverHandle = thermometer.RegisterObserver(temperatureLogger); // Register our Temperature Observer with the Observable\n    airPressureObserverHandle = thermometer.RegisterObserver(airPressureLogger); // Register our Air PRessure Observer with the Observable\n}\n\nvoid loop() {\n    thermometer.UpdateTemperature(); // We will update the temperature reading on the loop\n    thermometer.UpdateAirPressure(); // We will update the Air Pressure reading on the loop too.\n}\n```\nAnd that's all there is to it!\n\nWe now have a complete example of how a single `Observable` type can notify different `Observer` types using specific *Interfaces*.\n\nBut, there's one last feature you need to know about!\n\n### A single `Observer` can implement any number of *Observer Interfaces*!\nAs the title suggests, we can also define a single `Observer` type that implements any number of *Observer Interfaces*.\n\nThis is useful where your use-case requires that certain discrete objects in your code need to be notified of operations and state changes from multiple different (context-specific) objects elsewhere within your code.\n\nAs one example, let's presume we're programming a device which has a screen (a display module), and at any given moment it needs to display the Temperature, the Air Pressure, and perhaps even the Battery Level of your device.\n\nWe won't go into a complete working example (because there are a vast number of display modules out there, each with different SDKs and APIs) but we can at least define a *stub* for example:\n```cpp\n#include \u003cESPressio_IObserver.hpp\u003e\n#include \"ITemperatureObserver.hpp\"\n#include \"IAirPressureObserver.hpp\"\n#include \"IBatteryObserver.hpp\" // Hypothetical Battery Observer interface\n\nusing namespace ESPressio::Observable\n\nclass MyDisplay : public IObserver, public ITemperatureObserver, public IAirPressureObserver, public IBatteryObserver {\n    private:\n        float _currentBatteryPercent;\n        bool _isBatteryCharging;\n        int _currentTemperature;\n        int _lastTemperatureChange;\n        int _currentAirPressure;\n        int _lastAirPressureChange;\n\n        void Redraw() {\n            // Draw the Battery Level from _currentBatteryPercent on the screen\n            // Draw the Battery Charging Indicator based on _isBatteryCharging on the screen\n            // Draw the Current Temperature from _currentTemperature on the screen\n            // Draw the Last Temperature Change By from _lastTemperatureChange on the screen\n            // Draw the Current Air Pressure from _currentAirPressure on the screen\n            // Draw the Last Air Pressure Change By from _lastAirPressureChange on the screen\n        }\n    public:\n    // ITemperatureObserver implementations\n\n        void OnTemperatureChanged(int oldTemperature, int newTemperature) override {\n            _currentTemperature = newTemperature;\n            Redraw();\n        }\n\n        void OnTemperatureIncreased(int increasedBy) override {\n            _lastTemperatureChange = increasedBy;\n            Redraw();\n        }\n\n        void OnTemperatureDecreased(int decreasedBy) override {\n            _lastTemperatureChange = -decreasedBy // Gives us a negative number to render\n            Redraw();\n        }\n\n    // IAirPressureObserver implementations\n\n        void OnAirPressureChanged(int oldPressure, int newPressure) override {\n            _currentAirPressure = newPressure;\n            Redraw();\n        }\n\n        void OnAirPressureIncreased(int increasedBy) override {\n            _lastAirPressureChange = increasedBy;\n            Redraw();\n        }\n\n        void OnAirPressureDecreased(int decreasedBy) override {\n            _lastAirPressureChange = -decreasedBy; // Gives us a negative number to render\n            Redraw();\n        }\n\n    // IBatteryObserver implementations\n\n        void OnBatteryLevelChanged(float oldPercent, float newPercent) override {\n            _currentBatteryPercent = newPercent;\n            Redraw();\n        }\n\n        void OnBatteryIsCharging() override {\n            _isBatteryCharging = true;\n            Redraw();\n        }\n\n        void OnBatteryIsDischarging() override {\n            _isBatteryCharging = false;\n            Redraw();\n        }\n};\n```\nThe above `Observer` implementation will be notified of changes for all of:\n* Temperature\n* Air Pressure\n* Battery State\n\nPresuming an appropriate implementation in `Redraw()`, you will then display the latest data in response to any state change involving the aforementioned (Observed) state changes.\n\nSo, as you can see, a single `Observer` can implement as many `Observer` *Interfaces* as it requires to satisfy its distinct functional requirements.\n\nThis illustrates the powerful capabilities of ESPressio Observable, which we are absolutely certain will become an invaluable tool for developers intent on producing highly-maintainable, clean, and scalable software and firmware.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflowduino%2Fespressio-observable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflowduino%2Fespressio-observable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflowduino%2Fespressio-observable/lists"}