The Observer Pattern

02.06.07

The observer pattern is very useful and very easy to implement. It only requires two interfaces. One interface is the subject interface the other is the Observer.

    The subject contains three methods: AddObserver, RemoveObserver, and Notify. This interface is implemented by any class that needs to notify other objects that its state has changed.
Public Interface ISubject
    Sub AddObserver(ByRef ob As IObserver)
    Sub RemoveObserver(ByRef ob As IObserver)
    Sub Notify()
End Interface

The other interface is the observer it only contains one method Update, this interface is implemented by any class that needs to be notified by the subject object.

Public Interface IObserver
    Sub Update(ByVal subj As Object)
End Interface
In some examples of this pattern I have seen the subject is setup as an abstract class that you must inherit from, but I believe it limits the usefulness of the pattern. From what I read in the Head First Design Patterns book it says it is best to program to an interface instead of using inheritance. To illustrate this pattern I have created a simple program that monitors My Documents  folder on your computer and notifies a form when ever this folder's contents have changed. This is only an example, the way I implemented the subject object is a little weak but it will serve well enough.

I used the FileSystemWatcher as my base class and implemented the ISubject interface to serve as the subject. The class that implements the subject interface must contain a collection of observers the observers are added and removed using the AddObserver and RemoveObserver methods. The observers are notified by the Notify method witch iterates through the observer collection calling the Update method in the IObserver interface.

Imports System.IO
Public Class MyFileWatcher
    Inherits FileSystemWatcher
    Implements ISubject
Dim _observers As New ArrayList
Public Sub AddObserver(ByRef ob As IObserver) _
  Implements ISubject.AddObserver
    _observers.Add(ob)
End Sub
Public Sub Notify() _
  Implements ISubject.Notify
    Dim ob As IObserver
    For Each ob In _observers
        ob.Update(Me)
    Next
End Sub
Public Sub RemoveObserver(ByRef ob As IObserver) _
  Implements ISubject.RemoveObserver
    _observers.Remove(ob)
End Sub
Private Sub File_Changed _
 (ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles Me.Changed
    Me.Notify()
End Sub
Private Sub File_Created _
 (ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles Me.Created
    Me.Notify()
End Sub
Private Sub File_Deleted _
 (ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles Me.Deleted
    Me.Notify()
End Sub
Private Sub file_Renamed _
    (ByVal sender As Object, ByVal e As System.IO.RenamedEventArgs)Handles Me.Renamed
    Me.Notify()
End Sub
End Class

The observer is implemented as a form that contains List box this list box is Notified by the Subject and the Update method is ran witch updates the list with a valid list of files.

Imports fileWatcher
Imports System.IO
Public Class Form1
    Implements IObserver
    Private _path As String
    Private _allfiles As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Public Property Path() As String
        Get
            Return _path
        End Get
        Set(ByVal value As String)
            _path = value
        End Set
    End Property
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        _allfiles = My.Computer.FileSystem.GetFiles(_path)
        For Each singleFile As String In _allfiles
            Me.ListBox1.Items.Add(singleFile)
        Next
    End Sub
    Public Sub Update1(ByVal subj As Object) Implements fileWatcher.IObserver.Update
        Me.ListBox1.Items.Clear()
        _allfiles = My.Computer.FileSystem.GetFiles(_path)
        For Each singleFile As String In _allfiles
            Me.ListBox1.Items.Add(singleFile)
        Next
    End Sub
End Class

Lastly I created an MDI form that contains an instance of the FileWatcher class and creates a new Instance of the subject form every time a tool strip button is clicked. When ever a file in my documents is update all the child forms of the mdi are updated.

Imports System.Windows.Forms
Imports fileWatcher
Public Class MDIParent1
    Private Sub ShowNewForm(ByVal sender As Object, ByVal e As EventArgs) Handles NewToolStripButton.Click
        Dim FormObserver As New Form1
        FormObserver.MdiParent = Me
        FormObserver.Path = My.Computer.FileSystem.SpecialDirectories.MyDocuments
        _fileWatcherSubject.AddObserver(FormObserver)
        FormObserver.Show()
    End Sub
    Private Sub MDIParent1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        _fileWatcherSubject.Path = My.Computer.FileSystem.SpecialDirectories.MyDocuments
    End Sub
End Class
For complete Observer pattern code download at the following link.

fileWatcher.zip (116.6 KB)

Comments (3) -

2/8/2007 11:12:28 AM #

Luke

.Net and VB.Net have the Observer pattern built in. That's what events are.

Luke

2/8/2007 11:53:27 AM #

Aaron

.Net and VB.Net have the Observer pattern built in. That's what events are.

Yes, most event driven languages do have it built in and use the observer pattern for events, The observer pattern is sometimes referenced as the poor man’s event handling system.

Aaron

2/9/2007 3:14:27 AM #

Michal Talaga

Implemented or not I don't get what the hell notify method is doing on the ISubject interface. Shouldn't it be that notification is done because of some action/state change on the subject? Why would external clients need to invoke the Notify method?

Michal Talaga