Reactive and Functional Programming Design Patterns

Muhammet Ali AKBAY
The Startup

--

There are many ways to generate data and use it in programming. Infinite ways. I just want to talk about two. The two ways that i am in love with. To choose which one to implement is depended on your requirements. I will try to explain when to use them and their key differences in this article.

Supply on demand

  • Function/Supplier is passive.
  • Caller/Consumer is active.

This is the most common data supply way in programming. In this design, data being supplied by the supplier when the observer make demand for it. To implement this method is very simple. It can be built by just a method and a pair of controlled loops. A function gets called when caller is ready to handle the generated value, then function generates the data and returns it to the caller. Or it can be a little different by using Supplier/Consumer pair, a consumer callback method being passed to a supplier and then supplier calls the consumer with a parameter that contains the generated data after the data is ready. But the thing is, both of these alternatives generate data on the demand. Caller/Consumer determines when to get the data.

Example implementations,

  • Supplier/Function interfaces in Java
  • HTTP API implementations
  • Generators/Iterators in many languages

Of course there are many sub types of this design. You need to choose one of them by your requirements. Let me list a few,

  • If the data is finite, there must be common control mechanism which terminates the loop when it is end of the supply. A “null” return as an example. Of course it is not applicable always, some times supplied data can be null. A common way is to box the returned value with a control flag.
  • If supplier function calls another supplier inside its logic, it’s better to pass a callback function as a handler for consuming the iterating value. Supplier passes the consumer callback to the dependent suppliers.
  • If your implementation needs a loop which needs to be able to get paused and resume later, you may need to use another variable which contains next step on the data generation. And every supplier must pass this variable to the other recursive suppliers.

Some important things about this design,

  • Do not make the caller waiting for generation of the asynchronous data. If the data is not ready, just inform the caller about it. Also, it is already a bad idea to use this design with asynchronous data generation because of its nature. See the next chapter if you have to implement asynchronous one.

Share when ready

  • Observable/Subject is active.
  • Observer/Subscription is passive.

This design makes the data being emitted as soon as it gets generated. This approach makes it the best match to work with asynchronous data sources.

Examples,

  • ReactiveX Framework
  • Promises on JavaScript
  • Callbacks
  • Some Websocket or HTTP Polling API implementations

Cancellation

Any good data generation algorithm should be able to get cancelled. No one wants to have a function which loops forever since the result is not needed anymore. When a function is timed out or a generator loop is not necessary anymore it is very important to make it stop working, like a long taking HTTP request or an interval which emits current epoch time every second.

Or if we talk about reactive streams, an observable must stop and clean running functions when no active subscriber left. A well programmed Reactive streams gets cancelled when no active subscriber exists. But the key point is to keep reactive streams “reactive”.

Avoid using custom Observable instances.

It is out there somewhere.

Don’t give up searching for a common right way to implement an observable which acts exactly how you want. Always there is a reactive solution. If you choose to implement an observable which has its own custom subcription handler, it is so possible to miss some point that makes your observable keep working even while subscription got cancelled. And ofcourse, it makes the code less readable. Actually, this approach should be applied every thing about building somethings, especially in programming. I know (i know very well) that writing codes which does amazing things is a priceless joy but if you choose it way, stay ready for GIANT BUGS.

If you are using RxJS library, take a look at this geniusly designed decision tree, rxjs-dev.firebaseapp.com/operator-decision-tree .

Buffering

Buffering is another programming nightmare. It can help us when the supplier gets mad on emitting and consumer is not able to handle tons of data, or when consumer/observer doesn’t ready to take data. We can buffer generated data and let consumer take when it is ready. Sounds fair. But it is not. The failing part of this story is not buffering. It is the algorithm which makes us require buffering. If you are using buffers quite, it is time to review your design. You may be using “Supply on Demand” way while you need to use “Reactive Approach”, or opposite. If a data source is generates data faster than consumer’s limit, you should use “Supply on Demand” design. If consumer is ready to take new data, it should demand the data from supplier.

When multiple consumers/observers gets data from one supplier/observable, it is possible that one of the consumers/observers make the flow slower since the supplier/observable waits until all the consumers/observers process the data and ready to take a new one. You can use buffering techniques for these kind of situations. So, faster consumers/observers will be getting the new data while others still working on previous one. And allowed distance between previously processing and last one can be varying upon the available resources on the system or sizes of buffers.

Breaking the limitations: Hybrid Design

Think that, what if we could combine best parts of both design patterns? A single design which can be cancelled, buffered, stood away from bottlenecks is possible by combining these two patterns. And there is a good example for it, “Stream”s in NodeJS. Data generator can be supplier or observable, it is upon on your logic. If it is an observable and “highwatermark” (buffer’s maximum size) has been reached, the reader informs generator about it. So generator would stop generating data or inform another depended generator. If it is a supplier, reader asks for next data after each supply since subscription is active.

I will be writing more stories about reactive programming design patterns. Keep in touch. Until it, have good coding days.

--

--