PatternsCSharpExtraAddons/Patterns/Pattern_5-Наблюдатель(Observer)/Program.cs
Dvurechensky a4cd4b4ced 1.0
Main
2024-10-05 09:30:14 +03:00

328 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
/*
* ПАТТЕРНЫ ПОВЕДЕНИЯ
*
* Глава_5: Наблюдатель
*
* - определяет зависимость типа «один ко многим»» (один издатель ко многим подписчикам) между объектами
* таким образом, что при изменении состояния одного объекта все зависящие от него
* оповещаются об этом и автоматически обновляются
*
* -описывает правильные способы организации процесса подписки на определенные события
*/
/*
* Модель вытягивания (Pull model)
*/
/// <summary>
/// Обозреватели газеты
/// </summary>
abstract class Observer
{
public abstract void Update();
}
/// <summary>
/// Издатели газеты
/// </summary>
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();
}
}
/// <summary>
/// Конкретный издатель(издатель газеты) (наблюдаемый)
/// </summary>
class ConcreteSubject
: Subject
{
public string State { get; set; }
}
/// <summary>
/// Конкретный обозреватель (наблюдатель)
/// </summary>
class ConcreteObserver
: Observer
{
string observerState;
ConcreteSubject subject;
public ConcreteObserver(ConcreteSubject subject)
{
this.subject = subject;
}
public override void Update()
{
observerState = subject.State;
}
}
/*
* Модель проталкивания (Push model)
*/
/// <summary>
/// Обозреватели газеты
/// </summary>
abstract class Observer_Push
{
public abstract void Update(string state); // получает уведомление о новостях
}
/// <summary>
/// Издатели газеты
/// </summary>
abstract class Subject_Push
{
ArrayList observers = new ArrayList();
public abstract string State { get; set; }
/// <summary>
/// Добавить новость в газету
/// </summary>
/// <param name="observer">обозреватель газеты</param>
public void Attach(Observer_Push observer)
{
observers.Add(observer);
}
/// <summary>
/// Удалить новость из газеты
/// </summary>
/// <param name="observer">обозреватель газеты</param>
public void Detach(Observer_Push observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (Observer_Push observer in observers)
observer.Update(State);
}
}
/// <summary>
/// Конкретный издатель(издатель газеты)
/// </summary>
class ConcreteSubject_Push
: Subject_Push
{
public override string State { get; set; }
}
/// <summary>
/// Конкретный обозреватель
/// </summary>
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;
}
}
/// <summary>
/// Пример №2
/// </summary>
class Event { }
class LoggerEvent : Event { }
class AlertEvent : Event { }
/// <summary>
/// Класс, за которым можно наблюдать
/// получая оповещения о происходящих в нем событиях
/// </summary>
class TicTokUser : IObservable<Event>
{
public List<Subscription> Subscriptions { get; init; }
public TicTokUser() => Subscriptions = new List<Subscription>();
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<Event> observer)
{
var sub = new Subscription(this, observer);
Subscriptions.Add(sub);
return sub;
}
}
class Subscription : IDisposable
{
private TicTokUser user;
public IObserver<Event> observer;
public Subscription(TicTokUser user, IObserver<Event> observer)
{
this.user = user;
this.observer = observer;
}
/// <summary>
/// Удаление наблюдаемого класса из перечня подписавшихся
/// </summary>
public void Dispose() => user.Subscriptions.Remove(this);
}
/// <summary>
/// Конкретный подписчик
/// </summary>
class Observers : IObserver<Event>
{
public Observers()
{
var user = new TicTokUser();
var sub = user.Subscribe(this);
user.Alert();
user.Alert();
sub.Dispose();
user.Alert();
}
/// <summary>
/// Наблюдение окончено - больше ничего
/// происходить не будет
/// </summary>
public void OnCompleted() { }
/// <summary>
/// Обработка ошибки, явное оповещение
/// </summary>
/// <param name="error"></param>
public void OnError(Exception error) { }
/// <summary>
/// Событие при получении данных
/// </summary>
/// <param name="value"></param>
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");
}
}
/// <summary>
/// Логика социальной сети (делегаты)
/// </summary>
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<TicTokUsers, string> 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();
}
}