programming4us
programming4us
DESKTOP

Implement an Observer (aka Subscriber) Pattern

10/13/2010 5:16:13 PM
While most notification systems use .Net events to communicate, there are times when you want something a little more decoupled. For this, .Net 4 provides two interfaces to aid in implementing this common design pattern.

Use the IObserver<T> and IObservable<T> interfaces.

The IObservable<T> interface is implemented on the class that provides data for others to consume.

class DataGenerator : IObservable<int>
{
private List<IObserver<int>> _observers = new List<IObserver<int>>();
private int _lastPrime = -1;

//inherited from IObservable<T>
public IDisposable Subscribe(IObserver<int> observer)
{
_observers.Add(observer);
observer.OnNext(_lastPrime);
return observer as IDisposable;
}

//notifies all subscribers of the new data
private void NotifyData(int n)
{
foreach (IObserver<int> observer in _observers)
{
observer.OnNext(n);
}
}

//notifies all subscribers that no more data is coming
private void NotifyComplete()
{
foreach (IObserver<int> observer in _observers)
{
observer.OnCompleted();
}
}

private static Random rand = new Random();

//let's just generate some aribtrary data
public void Run()
{
for (int i=0;i<100;++i)
{
int n = rand.Next(1, Int32.MaxValue);
if (IsPrime(n))
{
_lastPrime = n;
NotifyData(n);
}
}
NotifyComplete();
}

private static bool IsPrime(Int32 number)
{
//check for evenness
if (number % 2 == 0)
{
if (number == 2)
return true;
return false;
}
//don't need to check past the square root
Int32 max = (Int32)Math.Sqrt(number);
for (Int32 i = 3; i <= max; i += 2)
{
if ((number % i) == 0)
{
return false;
}
}
return true;
}
}


The IObserver<T> is implemented on classes that want to know about the updates in the IObservable<T>-derived classes.

class DataObserver : IObserver<int>
{
//give it a name so we can distinguish it in the output
private string _name = "Observer";
#region IObserver<int> Members

public void OnCompleted()
{
Console.WriteLine(_name + ":Completed");
}
public void OnError(Exception error)
{
Console.WriteLine(_name + ": Error");
}

public void OnNext(int value)
{
Console.WriteLine(_name + ":Generated data {0}", value);
}

#endregion

public DataObserver(string observerName)
{
_name = observerName;
}
}


To tie them together, merely subscribe the observers to the data generator:

DataGenerator generator = new DataGenerator();

DataObserver observer1 = new DataObserver("O1");
DataObserver observer2 = new DataObserver("O2");

generator.Subscribe(observer1);
generator.Subscribe(observer2);

generator.Run();

The output is something like this:

O1:Generated data -1
O2:Generated data -1
O1:Generated data 597759749
O2:Generated data 597759749
O1:Generated data 369128117
O2:Generated data 369128117
O1:Generated data 650236453
O2:Generated data 650236453
O1:Generated data 2143508953
O2:Generated data 2143508953
O1:Generated data 298906169
O2:Generated data 298906169
O1:Generated data 1296076711
O2:Generated data 1296076711
O1:Generated data 1970737339
O2:Generated data 1970737339
O1:Completed
O2:Completed
Press any key to exit...

Other  
 
Video
PS4 game trailer XBox One game trailer
WiiU game trailer 3ds game trailer
Top 10 Video Game
-   Rise of Incarnates [PC] Zeus Trailer
-   Heroes Reborn | The Extraordinary Among Us (Preview)
-   Battleborn | E3 2015 Gameplay Demo
-   Fortnite [PC] Mac Showcase Trailer
-   Overwatch [PC] Zarya Gameplay Trailer
-   Tony Hawk's Pro Skater 5 [PS3/PS4/X360/XOne] THPS Is Back Trailer
-   Bombing Busters Trailer
-   Blade & Soul 'What is Blade & Soul?' Trailer
-   Cast of the Seven Godsends 'Plague Armour' Trailer
-   Guncraft X360 Trailer
-   Disgaea 5: Alliance of Vengeance | Official Trailer
-   XCOM 2 [PC] E3 2015 Gameplay Trailer
-   RONIN | Turn-Based Action Platformer
-   Balance Benny | Trailer
-   We Happy Few | An Uncle Jack Episode - Nighty Night, The Pied Piper of Hamlyn, Part1
Game of War | Kate Upton Commercial
programming4us
 
 
programming4us