Софтуерно Инженерство
Loading...
+ Нов въпрос
ArmenPotourlyan avatar ArmenPotourlyan 488 Точки

[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

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, а не как трябва да се пише код, така че подхождайте критично към него :)))

4
C# OOP Advanced 23/07/2016 14:05:22
silvi81 avatar silvi81 77 Точки

Много добре си описал всичко. Тъкмо сутринта гледах едно видео за Design Patterns :

https://softuni.bg/trainings/resources/video/7654/video-behavioral-patterns-3-february-2016-atanas-rusenov-high-quality-code-december-2015

След 59-та минута обясняват за Observer Pattern, aко някой иска още инфо по темата. Всъщност,  това което разбрах от видеото е, че Event-ите  в С# имплементират този патерн и са синтактична захар на езика, за да не се налага сами да го пишем. Преглеждайки мненията в stackoverflow, повечето са за ползване на event-и.

2
23/07/2016 15:46:27