Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Common Observer Pattern implementation using Generics

1.00/5 (1 vote)
1 Oct 2011CPOL 10.3K  
Customized implementation of the Observer pattern using Generics.

I read this link content and customized the implementation of the Observer pattern using Generics:
http://www.dofactory.com/Patterns/PatternObserver.aspx#_self2[^].


My implementation contains two Interfaces (IObserver and ISubject) and two concrete classes (Observer and Subject):



IObserver :
C#
public interface IObserver<T> where T : class 
{
    /// <summary>
    /// update observer according to update action that set to the observer
    /// </summary>
    void Update();

    /// <summary>
    /// element that observer should update it
    /// </summary>
    T Element { get; set; }

}


ISubject :
C#
public interface ISubject
{
    /// <summary>
    /// list of obsrvers
    /// </summary>
    IList<Dashboard.Framework.Observer.IObserver<object>> Observers { get; set; }

    /// <summary>
    /// common update action for observers which it's update action has set to null
    /// </summary>
    Action<ISubject, object> UpdateAction { get; set; }

    /// <summary>
    /// the state of subject
    /// </summary>
    object SubjectState { get; set; }

    /// <summary>
    /// attach a observer to this subject
    /// </summary>
    /// <param name="observer"></param>
    void Attach(Dashboard.Framework.Observer.IObserver<object> observer);

    /// <summary>
    /// detach a observer from this subject
    /// </summary>
    /// <param name="observer"></param>
    void Detach(Dashboard.Framework.Observer.IObserver<object> observer);

    /// <summary>
    /// create new observer with given element and common
    /// update action and attach it to this subject
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="element"></param>
    /// <param name="updateAction"></param>
    void AttachElement<T>(T element, Action<ISubject, 
         object> updateAction = null) where T : class;

    /// <summary>
    /// detach observer that contains given element
    /// </summary>
    /// <typeparam name="T"></typeparam>
    void DetachElement<T>(T element) where T : class;

    /// <summary>
    /// notify observers that they should update
    /// </summary>
    void Notify();
}


Observer :
C#
public class Observer<T> : IObserver<T> where T : class
{
    public T Element { get; set; }
    private ISubject Subject { get; set; }
    private Action<ISubject, T> Action { get; set; }

    public Observer(ISubject subject, T element, 
           Action<ISubject, T> action = null)
    {
        Subject = subject;
        Element = element;
        Action = action ?? subject.UpdateAction;
    }

    public void Update()
    {
        if(Action != null)
            Action.Invoke(Subject, Element);
    }
}


Subject :
C#
public class Subject : ISubject
{
    private IList<IObserver<dynamic>> _observers;

    public IList<IObserver<dynamic>> Observers
    {
        get
        {
            return _observers ?? (_observers = 
                   new List<IObserver<object>>());
        }
        set
        {
            _observers = value;
        }
    }

    public Action<ISubject, object> UpdateAction { get; set; }
    public object SubjectState { get; set; }

    public Subject(Action<ISubject, dynamic> action = null)
    {
        UpdateAction = action;
    }

    public void Attach(IObserver<dynamic> observer)
    {
        Observers.Add(observer);
    }

    public void Detach(IObserver<dynamic> observer)
    {
        Observers.Remove(observer);
    }

    public void AttachElement<T>(T element, Action<ISubject, 
           dynamic> updateAction = null) where T : class
    {
        if (element == null) 
            return;

        Observers.Add(new Observer<dynamic>(this, element, 
                      updateAction ?? UpdateAction));
    }

    public void DetachElement<T>(T element) where T : class 
    {
        var isExist = Observers.Any(p => p.Element == element);

        if (isExist)
            Observers.Remove(Observers.First(p => p.Element == element));
    }

    public void Notify()
    {
        foreach (var vo in Observers)
        {
            vo.Update();
        }
    }
}




For example
we have a ComboBox (cb) that containts two items(A, B) and three Textboxes (tb_A, tb_B_1, tb_B_2).
when text_A is selected, tb_A is visible and when text_B is selected, tb_B_1 and tb_B_2 are visible.

Before starting implementation, its better that we define an enum for SubjectState:

C#
enum VisibilitySubjectStates
{
    A = 1,
    B = 2
}


Then we can use Observer implementation in two way:


ONE ->

We should define our Subject:

C#
private ISubject _visibilitySubject = new Subject();


then we should attach our Observers:

C#
_visibilitySubject.Attach(
    new Observer<object>(_visibilitySubject, tb_A,
            (subject, frameworkElement) =>
                {
                    frameworkElement.Visibility = VisibilitySubjectStates.A.Equals(subject.SubjectState);
                }
    ));

_visibilitySubject.Attach(
    new Observer<object>(_visibilitySubject, tb_B_1,
            (subject, frameworkElement) =>
                {
                    frameworkElement.Visibility = VisibilitySubjectStates.B.Equals(subject.SubjectState);
                }
    ));

_visibilitySubject.Attach(
    new Observer<object>(_visibilitySubject, tb_B_2,
            (subject, frameworkElement) =>
                {
                    frameworkElement.Visibility = VisibilitySubjectStates.B.Equals(subject.SubjectState);
                }
    ));


Finally we should notify our observers when cb selection changed:

C#
cb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var comboBox = (ComboBox) sender;

    //We assume that Value of each Item in cb is type of VisibilitySubjectState
    _visibilitySubject.SubjectState = comboBox.SelectedValue;
    _visibilitySubject.Notify();
}





TWO ->

We should define our Subject without initializing:

C#
private ISubject _visibilitySubject;


Now we set the Tag property of Obesrvers with valid SubjectSatate:

C#
tb_A.Tag = VisibilitySubjectState.A;
tb_B_1.Tag = VisibilitySubjectState.B;
tb_B_2.Tag = VisibilitySubjectState.B;


We should initialize my Subject in constructor in this way (with common action for all observer that their UpdateAction is null):

C#
_visibilitySubject =
    new Subject(
        (subject, frameworkElement) =>
            {
                frameworkElement.Visibility = (frameworkElement.Tag ?? (VisibilitySubjectState)0).Equals(subject.SubjectState) ? Visibility.Visible : Visibility.Hidden;
            }
    );


then we should attach our Observers without UpdateAction:

C#
_visibilitySubject.Attach(new Observer<object>(_visibilitySubject, tb_A);

_visibilitySubject.Attach(new Observer<object>(_visibilitySubject, tb_B_1);

_visibilitySubject.Attach(new Observer<object>(_visibilitySubject, tb_B_2);


Finally we should notify our observers when cb selection changed:

C#
cb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var comboBox = (ComboBox) sender;

    //We assume that Value of each Item in cb is type of VisibilitySubjectState
    _visibilitySubject.SubjectState = comboBox.SelectedValue;
    _visibilitySubject.Notify();
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)