From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW Development Best Practices Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Observer Pattern

Intent

Define a one-to-many dependency between objects, so that when one object (Observable) changes state, all its dependents (Observer) are notified and updated automatically.

Motivation

A newspaper publisher goes into business and begins publishing newspaper. You subscribe to a particular publisher, and every time there's a new edition it gets delivered to you. As long as you remain a subscriber, you get new newspaper. Then you unsubscribe when you don't want papers anymore, and the stop being delivered. While the publisher remains in business, people, hotels, airlines and other businesses constantly subscribe and unsubscribe to the newspaper.

Implementation

The attached example uses a Text Display for illustration:

We have a text message object, the text message will notify all text displays when there is a new text updated. All text displays can register or unregister themselves.

First, we design the interface of Observable and Observer.

Observer Pattern Interface.png

We use an array of Observers as the private data of Observable. Because LabVIEW is a data flow language, the wire should be passed back when the data of the wire has been updated. In this application, Observers can register and unregister themselves. To avoid passing back wire after Observers register or unregister, we use DVR (Data Value Reference) to enclose the private data of Observable.

Observer Pattern Interface_LVClass.png

Then we create a Text Message class that inherit from Observable.

Observer Pattern Implement of Text Message.png

In this example, we want to display text normally and reversed, so we create two Observers.

Observer Pattern Implement.png

Normal Text and Reserve Text will override Observer's methods and use Observable as theirs private data, so that they can register or unregister themselves.

Comments
mike_nrao
Member
Member
on

Excellent work, but it could be simplified.  What you did is not incorrect, but you're mixing two variations of the observer pattern and could simplify your code by choosing one or the other.

There's no need to register to the Observable subject if your 'Notify Observers' method pushes the new value to each observer.  The way you designed it, you could do away with the Register and Unregister functions (and the Observable Object in the private class data) in each Observer, and your Demo VI could just call AddObserver for each observer and it still works brilliantly:

ObserverPatternWithPushNotification.png

Registering observers with subjects is another approach, but in this case, the notifyObservers function need not pass any data at all.  Since the Observers have a reference to the subject, they can simply call a 'get' accessor method on the subject in their update method.  Then you can remove the data pushing from notifiyObservers and the Update methods need no input.  I like this approach best because it allows you to add new data or state information to your subjects, and get it in your observers without having to modify methods in Observable or Observer.

The downside of this pattern in LabVIEW is that, since we can't implement interfaces like in Java, the subject is constrained to inherit from the 'Observable' class (and nothing else). 

Another aspect of your example code that may lead to some confusion is that your observer class implementations store a reference to a front panel control.  DVRs are confusing enough, why further complicate things with yet another reference in private data?  I suggest storing an actual data value in your concrete observer classes, then reading it at the end of the demo VI with accessor methods. 

Some of your wording is confusing to me, like "the wire should be passed back when the data of the wire has been updated."  Possibly consider revision (though I don't have any good suggestions).

Thanks for contributing this pattern!  A little refinement and it will be quite benefitial to the community.

unclad
NI Employee (retired)
on

Hi Micheal,

Thanks for your feedback.

Then you can remove the data pushing from notifiyObservers and the Update methods need no input.  I like this approach best because it allows you to add new data or state information to your subjects, and get it in your observers without having to modify methods in Observable or Observer.

Separate the data from interface is better.

Another aspect of your example code that may lead to some confusion is that your observer class implementations store a reference to a front panel control.  DVRs are confusing enough, why further complicate things with yet another reference in private data?  I suggest storing an actual data value in your concrete observer classes, then reading it at the end of the demo VI with accessor methods.

Like you said, it's better to return the value directly, I will refine it.

Some of your wording is confusing to me, like "the wire should be passed back when the data of the wire has been updated."

After observer registered by itself, you need pass the wire of subject back so that the new private data can be updated to subject. To avoid this, I choose to use DVR.

drjdpowell
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

I'd like to point out that the provided example is a Synchronous Observer Pattern.  The Observable calls methods on the Observers and must wait for them to complete even if they take a long time or are currently blocked by another method call.  This is equivalent to the Newspaper publisher sitting on your doorstep till you come home, waiting till you read the entire newspaper, and only then going on to the doorstep of the next subscriber.  And only after all subscribers read the paper can the publisher get back to creating next day's edition.   If everyone is at home and reads very quickly (as in the example) this is not an issue, but even a single slow reader is a problem.  So thus pattern can get into trouble.

The real-world Newspaper analogy is an Asynchronous Observer Pattern, a separate copy of the paper is delivered to each address, and all subscribers can read (or not read) in their own time, while the publisher goes about the business of creating next day's edition.  One can do this in LabVIEW using messages with asynchronous communication methods like queues serving as addresses. 

-- James

Active Participant
Active Participant
on

Excellent point, James!

jon_mcbee
Active Participant Active Participant
Active Participant
on

It seems like i can do this same thing using user events.  Is there a reason to do this with LVOOP over "standard" LV with user events?

Daklu
Active Participant
Active Participant
on

jmcbee wrote:

It seems like i can do this same thing using user events.  Is there a reason to do this with LVOOP over "standard" LV with user events?

It isn't a question of LVOOP vs. User Events.  User events are simply a mechanism for transporting messages (data) between different parts of your application.  Queues, FGs, global variables, etc are also transport mechanisms.  If you like using user events as your message transport you can use them to implement the observer pattern.  (Personally I prefer queues over user events.)

LVOOP vs "standard" LV is a religious debate I'd rather not get into.  I find that using LVOOP helps me write better applications in a shorter amount of time.  YMMV.

dangelo4
Member
Member
on

Does anyone have an example of asynchronous observer pattern?  Also one with not passing reference, but that data itself. 

drjdpowell
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

I use an asynchronous Observer pattern in my Messenging framework.  Processes use an ‘ObserverRegistry’ helper object to manage Notifications to registered observing processes.  Registration is via special registration messages.  See the “Simple Message Publishing” example.

Contributors