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
:
public interface IObserver<T> where T : class
{
void Update();
T Element { get; set; }
}
ISubject
:
public interface ISubject
{
IList<Dashboard.Framework.Observer.IObserver<object>> Observers { get; set; }
Action<ISubject, object> UpdateAction { get; set; }
object SubjectState { get; set; }
void Attach(Dashboard.Framework.Observer.IObserver<object> observer);
void Detach(Dashboard.Framework.Observer.IObserver<object> observer);
void AttachElement<T>(T element, Action<ISubject,
object> updateAction = null) where T : class;
void DetachElement<T>(T element) where T : class;
void Notify();
}
Observer
:
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
:
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:
enum VisibilitySubjectStates
{
A = 1,
B = 2
}
Then we can use Observer implementation in two way:
ONE ->
We should define our Subject:
private ISubject _visibilitySubject = new Subject();
then we should attach our Observers:
_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:
cb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = (ComboBox) sender;
_visibilitySubject.SubjectState = comboBox.SelectedValue;
_visibilitySubject.Notify();
}
TWO ->
We should define our Subject without initializing:
private ISubject _visibilitySubject;
Now we set the Tag property of Obesrvers with valid SubjectSatate:
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):
_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:
_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:
cb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = (ComboBox) sender;
_visibilitySubject.SubjectState = comboBox.SelectedValue;
_visibilitySubject.Notify();
}