Printed from www.rmfusion.com A Developer website designed for Developers

Observer Pattern Code Review

Code Download

Define the Pattern Interface and Handler Object

Define the IObserver interface to be used as the "blueprint" for the main Observer classes.

interface IObserver { void Update(Blogs state); }

The Observer class (object) represents an object that wants to be notified automatically when a particular event occurs.

class Observer : IObserver { String subscriber; Subject blogs; Interact visuals;
public Observer(Subject subject, String name) { this.blogs = subject; this.subscriber = name; visuals = new Interact(this.subscriber, this.Input); new Thread((ParameterizedThreadStart)delegate(object o) { Application.Run(visuals); }).Start(this);
while (visuals == null || visuals.IsHandleCreated) { Application.DoEvents(); Thread.Sleep(100); } blogs.Attach("Jim", this.Update); blogs.Attach("Eric", this.Update); blogs.Attach("Judith", this.Update); }
public void Update(Blogs state) { visuals.Output(String.Format("Blog from {0} on {1}", state.Name, state.Topic)); } public void Input(Object source, EventArgs e) { if (source == visuals.subscribeButton) { blogs.Attach(visuals.messageBox.Text, this.Update); visuals.wall.AppendText(String.Format("Subscribed to {0}\r\n", visuals.messageBox.Text)); } else { if (source == visuals.unSubscribeButton) { blogs.Detach(visuals.messageBox.Text, this.Update); visuals.wall.AppendText(String.Format("Unsubscribed from {0}\r\n", visuals.messageBox.Text)); } } } }

This object subscribes to a particular event. When the event occurs, the Update() method is called on the Observer class (object).

Define the Subject, Blogs and Simulator Objects

The Subject class (object) manages all subscriptions for a particular event.

class Subject { public delegate void Callback(Blogs blog);
Dictionary<String, Callback> notify = new Dictionary<String, Callback>(); Simulator simulator = new Simulator();
const Int32 speed = 4000;
private void Register(String blogger) { if (!this.notify.ContainsKey(blogger)) { this.notify[blogger] = delegate { }; } } private void Run() { foreach (Blogs blog in simulator) { this.Register(blog.Name); this.notify[blog.Name](blog); Thread.Sleep(speed); } } public void Attach(String blogger, Callback update) { this.Register(blogger); notify[blogger] += update; } public void Detach(String blogger, Callback update) { notify[blogger] -= update; } public void Go() { new Thread(new ThreadStart(this.Run)).Start(); } }

When the event occurs, the Subject class (object) will invoke the Update() method in the Observer class (object).

The Blogs class (object) represents events that can be subscribed to.

class Blogs { public String Name { get; set; } public String Topic { get; set; }
public Blogs(String name, String topic) { this.Name = name; this.Topic = topic; } }

The Simulator class (object) creates the events that can be subscribed to.

class Simulator : IEnumerable { Blogs[] bloggers = { new Blogs("Jim", "UML diagrams"), new Blogs("Eric", "Iterators"), new Blogs("Eric", "Extension Methods"), new Blogs("Judith", "Delegates"), new Blogs("Eric", "Type interface"), new Blogs("Jim", "Threads"), new Blogs("Eric", "Lamda expressions"), new Blogs("Judith", "Anonymous properties"), new Blogs("Eric", "Generic delegates"), new Blogs("Jim", "Efficiency") };
IEnumerator IEnumerable.GetEnumerator() { foreach (Blogs blog in bloggers) { yield return blog; } } }

Define the Windows Form Graphical User Interface

The Interact class (object) creates the GUI interface to the application.

class Interact : Form { public TextBox wall; public Button subscribeButton; public Button unSubscribeButton; public TextBox messageBox;
String name;
public Interact(String name, EventHandler Input) { Control.CheckForIllegalCrossThreadCalls = true;
this.name = name; wall = new TextBox(); wall.Multiline = true; wall.Location = new Point(0, 30); wall.Width = 300; wall.Height = 200; wall.AcceptsReturn = true; wall.Dock = DockStyle.Fill; this.Text = name; this.Controls.Add(wall);
Panel p = new Panel(); messageBox = new TextBox(); messageBox.Width = 120; p.Controls.Add(messageBox);
subscribeButton = new Button(); subscribeButton.Left = messageBox.Width; subscribeButton.Text = "Subscribe"; subscribeButton.Click += new EventHandler(Input); p.Controls.Add(subscribeButton);
unSubscribeButton = new Button(); unSubscribeButton.Left = messageBox.Width + subscribeButton.Width; unSubscribeButton.Text = "Unsubscribe"; unSubscribeButton.Click += new EventHandler(Input); p.Controls.Add(unSubscribeButton);
p.Height = subscribeButton.Height; p.Height = unSubscribeButton.Height; p.Dock = DockStyle.Top; this.Controls.Add(p); } public void Output(String message) { if (this.InvokeRequired) { this.Invoke((MethodInvoker)delegate() { Output(message); }); } else { wall.AppendText(String.Format("{0}\r\n", message)); this.Show(); } } }

Use the Observer Pattern

The Program class creates the Subject object, which in turn invokes the Simulator object where events are set up that can be subscribed to.

class Program { static void Main(string[] args) { Subject subject = new Subject(); Observer observer = new Observer(subject, "Thabo"); Observer observer2 = new Observer(subject, "Ellen"); subject.Go(); } }

The Observer objects are created and automatically subscribe to all the available events. The Go() method will raise the appropriate events, and each Observer will in turn be notified.