328 lines
9.0 KiB
C#
328 lines
9.0 KiB
C#
|
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();
|
|||
|
}
|
|||
|
}
|