using System.Collections; /* * ПАТТЕРНЫ ПОВЕДЕНИЯ * * Глава_5: Наблюдатель * * - определяет зависимость типа «один ко многим»» (один издатель ко многим подписчикам) между объектами * таким образом, что при изменении состояния одного объекта все зависящие от него * оповещаются об этом и автоматически обновляются * * -описывает правильные способы организации процесса подписки на определенные события */ /* * Модель вытягивания (Pull model) */ /// /// Обозреватели газеты /// abstract class Observer { public abstract void Update(); } /// /// Издатели газеты /// abstract class Subject { ArrayList observers = new ArrayList(); public void Attach(Observer observer) { observers.Add(observer); } public void Detach(Observer observer) { observers.Remove(observer); } public void Notify() { foreach (Observer observer in observers) observer.Update(); } } /// /// Конкретный издатель(издатель газеты) (наблюдаемый) /// class ConcreteSubject : Subject { public string State { get; set; } } /// /// Конкретный обозреватель (наблюдатель) /// class ConcreteObserver : Observer { string observerState; ConcreteSubject subject; public ConcreteObserver(ConcreteSubject subject) { this.subject = subject; } public override void Update() { observerState = subject.State; } } /* * Модель проталкивания (Push model) */ /// /// Обозреватели газеты /// abstract class Observer_Push { public abstract void Update(string state); // получает уведомление о новостях } /// /// Издатели газеты /// abstract class Subject_Push { ArrayList observers = new ArrayList(); public abstract string State { get; set; } /// /// Добавить новость в газету /// /// обозреватель газеты public void Attach(Observer_Push observer) { observers.Add(observer); } /// /// Удалить новость из газеты /// /// обозреватель газеты public void Detach(Observer_Push observer) { observers.Remove(observer); } public void Notify() { foreach (Observer_Push observer in observers) observer.Update(State); } } /// /// Конкретный издатель(издатель газеты) /// class ConcreteSubject_Push : Subject_Push { public override string State { get; set; } } /// /// Конкретный обозреватель /// class ConcreteObserver_Push : Observer_Push { string observerState; ConcreteSubject_Push subject; public ConcreteObserver_Push(ConcreteSubject_Push subject) { this.subject = subject; } public override void Update(string state) { observerState = subject.State; } } /// /// Пример №2 /// class Event { } class LoggerEvent : Event { } class AlertEvent : Event { } /// /// Класс, за которым можно наблюдать /// получая оповещения о происходящих в нем событиях /// class TicTokUser : IObservable { public List Subscriptions { get; init; } public TicTokUser() => Subscriptions = new List(); public void Alert() { Random r = new Random(); foreach (var sub in Subscriptions) { Event e = r.Next(2) switch { 0 => new LoggerEvent(), _ => new AlertEvent() }; sub.observer.OnNext(e); } } /* - IObserver - тот, кто хочет получать уведомления от наблюдающего класса - IDisposable - как правило реализуют с целью возможной отписки */ public IDisposable Subscribe(IObserver observer) { var sub = new Subscription(this, observer); Subscriptions.Add(sub); return sub; } } class Subscription : IDisposable { private TicTokUser user; public IObserver observer; public Subscription(TicTokUser user, IObserver observer) { this.user = user; this.observer = observer; } /// /// Удаление наблюдаемого класса из перечня подписавшихся /// public void Dispose() => user.Subscriptions.Remove(this); } /// /// Конкретный подписчик /// class Observers : IObserver { public Observers() { var user = new TicTokUser(); var sub = user.Subscribe(this); user.Alert(); user.Alert(); sub.Dispose(); user.Alert(); } /// /// Наблюдение окончено - больше ничего /// происходить не будет /// public void OnCompleted() { } /// /// Обработка ошибки, явное оповещение /// /// public void OnError(Exception error) { } /// /// Событие при получении данных /// /// public void OnNext(Event value) { if (value is LoggerEvent) Console.WriteLine("Произошло событие OnNext value is LoggerEvent"); if (value is AlertEvent) Console.WriteLine("Произошло событие OnNext value is AlertEvent"); } } /// /// Логика социальной сети (делегаты) /// class MediaFile { public MediaFile (string fileName) => FileName = fileName; public string FileName { get; set; } } class Video : MediaFile { public Video(string fileName) : base(fileName) { } } class Accaunt { public string Nick { get; set; } } class TicTokUsers : Accaunt { protected event Action followers; public void Subscribe(TicTokUsers user) { Console.WriteLine($"{user.Nick} подписался на {Nick}"); followers += user.Alert; } public void UnSubscribe(TicTokUsers user) { Console.WriteLine($"{user.Nick} отдписался на {Nick}"); followers -= user.Alert; } public void Alert(TicTokUsers sender, string info) { if (sender != this) Console.WriteLine($"Лента {Nick}: У {sender.Nick} {info}"); else Console.WriteLine($"У меня ({Nick}) {info}"); } public void VideoPublishing(MediaFile media) { var fn = $"вышло видео '{media.FileName}'"; Alert(this, fn); followers?.Invoke(this, fn); } } class Program { public static void Main(string[] args) { Console.WriteLine("Pull model"); ConcreteSubject subject_pull = new ConcreteSubject(); //инициализируем издателя газеты subject_pull.Attach(new ConcreteObserver(subject_pull)); //публикуем новость для читателя subject_pull.Attach(new ConcreteObserver(subject_pull)); //публикуем новость для читателя subject_pull.State = "Some state ..."; subject_pull.Notify(); Console.WriteLine("Push model"); ConcreteSubject subject_push = new ConcreteSubject(); subject_push.Attach(new ConcreteObserver(subject_push)); //публикуем новость для читателя subject_push.Attach(new ConcreteObserver(subject_push)); //публикуем новость для читателя subject_push.State = "Some state ..."; subject_push.Notify(); _ = new Observers(); //Пример 2 на TicTok TicTokUsers user1 = new() { Nick = "user_1" }; TicTokUsers user2 = new() { Nick = "user_2" }; user1.VideoPublishing(new MediaFile("Misl 1")); user1.Subscribe(user2); user1.VideoPublishing(new MediaFile("Misl 2")); user1.VideoPublishing(new MediaFile("Misl 3")); Console.WriteLine(); user2.Subscribe(user1); user2.VideoPublishing(new MediaFile("History 1")); Console.ReadLine(); } }