Thursday, September 21, 2006

Observer Pattern In .NET

Observer pattern is a publish-subscribe pattern. It allows an object (observer) to watch another object (subject), and be notified when subject is changed.

Java has defined java.util.Observable class and java.util.Observer interface since SDK 1.0:
public class Observable extends Object
{
public void addObserver(Observer o) {}
public void deleteObserver(Observer o) {}
public void notifyObservers() {}
//...
}

public interface Observer
{
public void update(Observable o, Object arg){}
}
By inheriting Observable class and implementing Observer interface, the observer pattern is enforced. There's no Observer and Observable interface/class in .NET BCL. However it's not a difficult task to implement the observer pattern in .NET. Following code demos a simple .NET implementation of observer pattern:
using System;
using System.Text;
using System.Collections;
using System.Threading;

class Program
{
public interface IObserver
{
void Update(string state);
}

public interface IObservable
{
void AddObserver(IObserver o);
void NotifyObservers();
}

public class WeatherObservable : IObservable
{
private volatile int _temperature;
private ArrayList _observers = new ArrayList();
private Random rand = new Random();

public WeatherObservable()
{
new Thread(new ThreadStart(WeatherWatcher)).Start();
}

public void WeatherWatcher()
{
Console.WriteLine("Weather is changing ...");
while (true)
{
_temperature = rand.Next(-20, 40);
NotifyObservers();
Thread.Sleep(1000);
}
}

public void AddObserver(IObserver obj)
{
lock (_observers.SyncRoot)
{
_observers.Add(obj);
}
}

public void NotifyObservers()
{
lock (_observers.SyncRoot)
{
foreach (IObserver observer in _observers)
{
observer.Update(_temperature.ToString());
}
}
}
}

public class WeatherDisplayer : IObserver
{
public void Update(string state)
{
int temperature = int.Parse(state);
Console.WriteLine("Temperature now is {0}", temperature);
}
}

public class WeatherAlarm : IObserver
{
public void Update(string state)
{
int temperature = int.Parse(state);
if (temperature < -10)
{
Console.WriteLine("\nCold Alert! Temperature is {0}\n", temperature);
}
else if (temperature > 30)
{
Console.WriteLine("\nHot Alert! Temperature now is {0}\n", temperature);
}
}
}

static void Main(string[] args)
{
WeatherObservable ww = new WeatherObservable();
ww.AddObserver(new WeatherDisplayer());
ww.AddObserver(new WeatherAlarm());
Console.Read();
}
}
The WeatherObservable class simulates the weather changes, and keeps sending the updated temperatures to observers (subscriber). There are two subscribers in the demo code; one shows the latest temperature and the other shows alert information when temperature is in certain condition. When running the application, a console would look like:

The above code is just a clone of Java implementation. For concise demonstration purpose, not all functions are included and synchronized notification is used.

Actually .NET uses event concept to resolve the publish-subscribe problem. An observable subject publishes its events to its subscribers which registered those events by safe and typed functions (delegate). The implementation of observer pattern can be simplified by using .NET event:
using System;
using System.Text;
using System.Collections;
using System.Threading;

class Program
{
public class WeatherObservable
{
private volatile int _temperature;
Random rand = new Random();

public delegate void UpdateDelegate(int temperature);
public event UpdateDelegate TemperatureUpdateEvent;

public WeatherObservable()
{
new Thread(new ThreadStart(WeatherWatcher)).Start();
}

public void WeatherWatcher()
{
Console.WriteLine("Weather is changing ...");
while (true)
{
_temperature = rand.Next(-20, 40);

if (TemperatureUpdateEvent != null)
{
TemperatureUpdateEvent(_temperature);
}
Thread.Sleep(1000);
}
}
}

public class WeatherDisplayer
{
public void TemperatureUpdated(int temperature)
{
Console.WriteLine("Temperature now is {0}", temperature);
}
}

public class WeatherAlarm
{
public static void TemperatureUpdated(int temperature)
{
if (temperature < -10)
{
Console.WriteLine("\nCold Alert! Temperature is {0}\n", temperature);
}
else if (temperature > 30)
{
Console.WriteLine("\nHot Alert! Temperature now is {0}\n", temperature);
}
}
}

static void Main(string[] args)
{
WeatherObservable ww = new WeatherObservable();
WeatherDisplayer displayer = new WeatherDisplayer();
ww.TemperatureUpdateEvent += new WeatherObservable.UpdateDelegate(displayer.TemperatureUpdated);
ww.TemperatureUpdateEvent += new WeatherObservable.UpdateDelegate(WeatherAlarm.TemperatureUpdated);
Console.Read();
}
}
Note: as shown in the code, both class member functions or static functions can be registered to an event.

Tuesday, September 19, 2006

Simple iFrame Popup Window

<style type="text/css">
.popup {
        position:fixed;
        clear:both;
        height: 400px;
        width: 600px;
        z-index: 2;
        border: solid;
        background-color: white;
}
.grayBG {
        position: fixed;
        clear:both;
        top: 0px;
        left: 0px;
        right: 0px;
        bottom: 0px;
        overflow: hidden;
        padding: 0;
        margin: 0;
        background-color: #000;
        z-index: 1;
        filter:alpha(opacity=70);
        opacity:0.7;
        -moz-opacity:0.7;
}
</style>
<script language="javascript" type="text/javascript">
    function showDiv(id)
    {
        var div = document.getElementById("divPop");
        div.style.display = "block";
        var divFrame = document.getElementById("divFrame");
        divFrame.src = "PopupPage.aspx?id=" + id;        
        //document.body.style.scrolling = "no";
    }
    function hideDiv()
    {
        var div = document.getElementById("divPop");
        div.style.display = "none";
        //document.body.style.scrolling = "auto";
    } 
</script>
<div id="divPop" style="display: none;">
 <div class="grayBG" runat="server"></div>
 <div runat="server" class="popup">
    <a onclick="hideDiv();"></a><br />
    <iframe id="divFrame" runat="server" width="100%" height="100%">
    </iframe>
 </div>
</div>