[Useful Info][C# OOP Advanced] The Observer Pattern vs C# Events
Здравейте, колеги,
Тези дни разглеждах какво представляват event-ите в C# и се сетих, че съм виждал нещо подобно в Observer Design Pattern-a. Реших да опитам да напиша един прост пример с Observer и после да го преработя да използва вградените възможности на C# - event-ите. Ето какво се получи:
Observer Pattern-ът свързва много обекти (напр. YouTube потребители) към един обект (напр. YouTube канал) така, че ако обектът промени състоянието си (напр. наличност на ново видео в YouTube канала), свързаните към него обекти да бъдат уведомени (YouTube абонатите получават нотификация).
http://www.dofactory.com/net/observer-design-pattern
Как се реализира това? Имаме интерфейс ISubject, който дефинира методи за добавяне Attach и премахване Detach на абонати и метод за уведомяване Notify. Също така имаме интерфейс за абонатите IObserver, който дефинира метод Update. Конкретният Subject клас държи колекция от абонати (напр. List<IObserver>) и в Notify() минава през всеки един от тях и извиква Update(). Конкретните Observer класове пък дефинират какво точно да се случва в Update метода.
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public interface IObserver
{
void Update(ISubject sender);
}
Нека имаме конкретен клас Lecture, който имплементира ISubject, и конкретен клас Student, който импрементира IObserver. При всяка смяна на името на инструктора, всички студенти, които са закачени към лекцията, се уведомяват в метода Notify.
public class Lecture : ISubject
{
private List<IObserver> observers;
private string trainer;
public Lecture(string topic, string trainer)
{
this.Topic = topic;
this.Trainer = trainer;
this.observers = new List<IObserver>();
}
public string Topic { get; }
public string Trainer
{
get { return this.trainer; }
set
{
this.trainer = value;
this.Notify();
}
}
public void Attach(IObserver observer)
{
this.observers.Add(observer);
}
public void Detach(IObserver observer)
{
this.observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in this.observers)
{
observer.Update(this);
}
}
}
public class Student : IObserver
{
public string Name { get; set; }
public void Update(ISubject sender)
{
var trainer = (sender as Lecture).Trainer;
Console.WriteLine($"{this.Name} does not like the new trainer {trainer}!");
}
}
В Main метода:
var lecture = new Lecture("C# events", "Ivaylo Kenov");
var studentPesho = new Student() { Name = "Pesho" };
var studentGosho = new Student() { Name = "Gosho" };
lecture.Attach(studentPesho);
lecture.Attach(studentGosho);
lecture.Trainer = "Chuck Norris";
// Pesho does not like the new trainer Chuck Norris!
// Gosho does not like the new trainer Chuck Norris!
lecture.Detach(studentPesho);
lecture.Trainer = "Bill Gates";
// Gosho does not like the new trainer Bill Gates!
Как изглежда същото нещо със C# event-ите? Нямаме нужда от старите интерфейси, така че методите Attach, Detach, Notify в класа Lecture вече са излишни. Дефинираме си event TrainerNameChanged и в set-ъра на свойството Trainer го вдигаме(извикваме), т.е. всеки път, когато се смени името на лектора, ще бъде обработено въпросното събитие(стига да има прикачени студенти).
public class Lecture
{
public event EventHandler TrainerNameChanged;
private string trainer;
public Lecture(string topic, string trainer)
{
this.Topic = topic;
this.Trainer = trainer;
}
public string Topic { get; }
public string Trainer
{
get { return this.trainer; }
set
{
this.trainer = value;
TrainerNameChanged?.Invoke(this, EventArgs.Empty);
}
}
}
Как става закачането и откачането на студенти? Класът Student дефинира метод за обработка на събитието OnTrainerNameChanged и старите методи Attach и Detach са заменени от += и –= оператори:
public class Student
{
public string Name { get; set; }
public void OnTrainerNameChanged(object sender, EventArgs e)
{
var trainer = (sender as Lecture).Trainer;
Console.WriteLine($"{this.Name} does not like the new trainer {trainer}!");
}
}
В Main метода:
var lecture = new Lecture("C# events", "Ivaylo Kenov");
var studentPesho = new Student() { Name = "Pesho" };
var studentGosho = new Student() { Name = "Gosho" };
lecture.TrainerNameChanged += studentPesho.OnTrainerNameChanged;
lecture.TrainerNameChanged += studentGosho.OnTrainerNameChanged;
lecture.Trainer = "Chuck Norris";
// Pesho does not like the new trainer Chuck Norris!
// Gosho does not like the new trainer Chuck Norris!
lecture.TrainerNameChanged -= studentPesho.OnTrainerNameChanged;
lecture.Trainer = "Bill Gates";
// Gosho does not like the new trainer Bill Gates!
Имайте предвид, че примерът е доста прост и целта му е да демонстрира прехода от Observer Pattern към C# events, а не как трябва да се пише код, така че подхождайте критично към него :)))