Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#
Tip/Trick

Common Observer Pattern implementation using Generics

Rate me:
Please Sign up or sign in to vote.
1.00/5 (1 vote)
1 Oct 2011CPOL 9.8K   3   1
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)


Written By
Software Developer mp3lyric.us
Iran (Islamic Republic of) Iran (Islamic Republic of)
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralReason for my vote of 1 Too overly complicated. Just use INo... Pin
SledgeHammer011-Oct-11 8:17
SledgeHammer011-Oct-11 8:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.