Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active December 28, 2025 11:44
Show Gist options
  • Select an option

  • Save sunmeat/ec2194a8c89833d9a89ac8ed5f8fb426 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/ec2194a8c89833d9a89ac8ed5f8fb426 to your computer and use it in GitHub Desktop.
pattern observer - 4 examples
using System.Text;
// Instagram v.0.01
class Program
{
static void Main()
{
Console.OutputEncoding = Encoding.UTF8;
var Alex = new User() { Name = "Олександр" };
var Olha = new InstagramFollower();
var Mykola = new InstagramFollower();
Alex.AddSubscriber(Olha);
Alex.AddSubscriber(Mykola);
Alex.MakeStory("Красивий захід сонця!");
Alex.MakeReel("Веселий танець!");
Alex.PostPhoto("Нове фото профілю!");
}
}
// підписник
interface ISubscriber
{
void Like(string content);
void Comment(string content);
void Message(string content);
void Ignore(string content);
void Unsubscribe();
}
// видавець
abstract class Publisher
{
protected List<ISubscriber> subscribers = new List<ISubscriber>();
public void AddSubscriber(ISubscriber subscriber)
{
subscribers.Add(subscriber);
}
public void RemoveSubscriber(ISubscriber subscriber)
{
subscribers.Remove(subscriber);
}
public void NotifySubscribers(string content)
{
var random = new Random(); // рандомна дія підписника
// насправді, тут би вистачило викликати просто subscriber.Update(content);,
// і хай би підписник сам вирішував, що робити, але поки для прикладу я зробив різні дії
foreach (var subscriber in subscribers)
{
int randomAction = random.Next(5);
if (randomAction == 0) subscriber.Like(content);
else if (randomAction == 1) subscriber.Comment(content);
else if (randomAction == 2) subscriber.Message(content);
else if (randomAction == 3) subscriber.Ignore(content);
else subscriber.Unsubscribe();
}
}
}
class User : Publisher
{
public string? Name { get; set; }
public void MakeStory(string content)
{
Console.WriteLine(Name + " створив сторіс: " + content);
NotifySubscribers(content);
}
public void MakeReel(string content)
{
Console.WriteLine(Name + " виклав рілс: " + content);
NotifySubscribers(content);
}
public void PostPhoto(string content)
{
Console.WriteLine(Name + " опублікував фото: " + content);
NotifySubscribers(content);
}
}
class InstagramFollower : ISubscriber
{
public void Like(string content)
{
Console.WriteLine("Лайк!");
}
public void Comment(string content)
{
Console.WriteLine("Коментар!");
}
public void Message(string content)
{
Console.WriteLine("Надіслано повідомлення!");
}
public void Ignore(string content)
{
Console.WriteLine("Проігноровано");
}
public void Unsubscribe()
{
Console.WriteLine("Відписався від користувача");
// технічно, тут треба було б якось передати посилання на юзера, від якого відписуємося,
// але для простоти прикладу я це поки пропустив
}
}
// ========================================================================================================
using System.Text;
// Instagran v.0.02
class Program
{
static void Main()
{
Console.OutputEncoding = Encoding.UTF8;
var andriy = new User();
var petro = new InstagramFollower();
var maria = new InstagramFollower();
// тепер підписники можуть підписуватися на видавців, а не видавець схвалює підписників
petro.AddPublisher(andriy);
maria.AddPublisher(andriy);
andriy.MakeStory("Красивий захід сонця!");
andriy.MakeReel("Веселий танець!");
andriy.PostPhoto("Нове фото профілю!");
}
}
// інтерфейс підписника
interface ISubscriber
{
void Like(string content);
void Comment(string content);
void Message(string content);
void Ignore(string content);
void Unsubscribe(Publisher publisher);
}
// абстрактний клас видавця
abstract class Publisher
{
protected List<ISubscriber> subscribers = new List<ISubscriber>();
public void AddSubscriber(ISubscriber subscriber)
{
if (!subscribers.Contains(subscriber)) // уникаємо дублювання підписників
{
subscribers.Add(subscriber);
}
}
public void RemoveSubscriber(ISubscriber subscriber)
{
subscribers.Remove(subscriber);
}
protected void NotifySubscribers(string content)
{
var random = new Random();
List<ISubscriber> copyOfSubscribers = new(subscribers);
foreach (var subscriber in copyOfSubscribers)
{
int randomAction = random.Next(0, 5);
switch (randomAction)
{
case 0:
subscriber.Like(content);
break;
case 1:
subscriber.Comment(content);
break;
case 2:
subscriber.Message(content);
break;
case 3:
subscriber.Ignore(content);
break;
case 4:
subscriber.Unsubscribe(this); // тепер підписник може відписатися сам
break;
default:
break;
}
}
}
}
// приклад класу користувача-видавця
class User : Publisher
{
public void MakeStory(string content)
{
Console.WriteLine($"користувач створив сторіс: {content}");
NotifySubscribers(content);
}
public void MakeReel(string content)
{
Console.WriteLine($"користувач створив рілс: {content}");
NotifySubscribers(content);
}
public void PostPhoto(string content)
{
Console.WriteLine($"користувач опублікував фото: {content}");
NotifySubscribers(content);
}
}
// приклад класу користувача-підписника
class InstagramFollower : ISubscriber
{
List<Publisher> publishers = new List<Publisher>();
public void AddPublisher(Publisher publisher)
{
if (!publishers.Contains(publisher))
{
publishers.Add(publisher);
publisher.AddSubscriber(this);
}
}
public void Like(string content)
{
Console.WriteLine("Лайкнуто!");
}
public void Comment(string content)
{
Console.WriteLine("Прокоментовано!");
}
public void Message(string content)
{
Console.WriteLine("Надіслано повідомлення!");
}
public void Ignore(string content)
{
Console.WriteLine("Проігноровано");
}
public void Unsubscribe(Publisher publisher)
{
Console.WriteLine("Відписано від користувача");
if (publishers.Contains(publisher))
{
publisher.RemoveSubscriber(this);
publishers.Remove(publisher);
}
}
}
// ========================================================================================================
using System.Text;
// Instagram v.0.03
class Program
{
static void Main()
{
Console.OutputEncoding = Encoding.UTF8;
var andriy = new InstagramUser();
var petro = new InstagramUser();
var maria = new InstagramUser();
petro.Subscribe(andriy);
maria.Subscribe(andriy);
andriy.MakeStory("Красивий захід сонця!");
andriy.MakeReel("Веселий танець!");
andriy.PostPhoto("Нове фото профілю!");
}
}
// інтерфейс підписника (наближено до класичного observer: лише update від видавця)
interface ISubscriber
{
void Update(IPublisher publisher, string content);
}
// інтерфейс видавця (наблизжено до subject: attach, detach, notify)
interface IPublisher
{
void AddSubscriber(ISubscriber subscriber);
void RemoveSubscriber(ISubscriber subscriber);
void NotifySubscribers(string content);
}
// звісно, як користувачі Instagram, ми можемо бути і підписниками, і видавцями одночасно
class InstagramUser : IPublisher, ISubscriber
{
private List<ISubscriber> subscribers = new List<ISubscriber>(); // мої підписники
private List<IPublisher> followedPublishers = new List<IPublisher>(); // кого я читаю
// реалізація IPublisher
public void AddSubscriber(ISubscriber subscriber)
{
if (!subscribers.Contains(subscriber))
{
subscribers.Add(subscriber);
}
}
public void RemoveSubscriber(ISubscriber subscriber)
{
subscribers.Remove(subscriber);
}
public void NotifySubscribers(string content)
{
List<ISubscriber> copyOfSubscribers = new(subscribers);
foreach (var subscriber in copyOfSubscribers)
{
// передаємо this як посилання на Видавця (а то раптом підписник захоче відписатися)
subscriber.Update(this, content);
}
}
// реалізація ISubscriber: наближено до класичного update, з випадковою логікою дій
public void Update(IPublisher publisher, string content)
{
var random = new Random();
int randomAction = random.Next(5);
switch (randomAction)
{
case 0:
Like(content);
break;
case 1:
Comment(content);
break;
case 2:
Message(content);
break;
case 3:
Ignore(content);
break;
case 4:
Unsubscribe(publisher);
break;
default:
break;
}
}
// методи для підписки на/відписки від Видавця (допоміжні, не з інтерфейсів)
public void Subscribe(IPublisher publisher)
{
if (!followedPublishers.Contains(publisher))
{
followedPublishers.Add(publisher);
publisher.AddSubscriber(this);
}
}
public void Unsubscribe(IPublisher publisher)
{
if (followedPublishers.Contains(publisher))
{
Console.WriteLine("З мене досить! Відписка!");
publisher.RemoveSubscriber(this);
followedPublishers.Remove(publisher);
}
}
// методи реакцій на сповіщення (внутрішні)
private void Like(string content)
{
Console.WriteLine("Лайкнуто!");
}
private void Comment(string content)
{
Console.WriteLine("Прокоментовано!");
}
private void Message(string content)
{
Console.WriteLine("Надіслано повідомлення!");
}
private void Ignore(string content)
{
Console.WriteLine("Проігноровано");
}
// методи публікації контенту
public void MakeStory(string content)
{
Console.WriteLine($"користувач створив сторіс: {content}");
NotifySubscribers(content);
}
public void MakeReel(string content)
{
Console.WriteLine($"користувач створив рілс: {content}");
NotifySubscribers(content);
}
public void PostPhoto(string content)
{
Console.WriteLine($"користувач опублікував фото: {content}");
NotifySubscribers(content);
}
}
// ========================================================================================================
// ну і ще один шикарний приклад до кучі:
// Weather Example, нехай проблеми та негоди не роблять вам в житті погоди :)
using System.Text.RegularExpressions;
namespace ObserverPatternExample
{
interface Observer
{
void Update(string city, string weatherInfo);
}
interface Subject
{
void RegisterObserver(Observer observer);
void RemoveObserver(Observer observer);
void NotifyObservers();
}
interface DisplayElement
{
void Display();
}
class WttrWeatherData : Subject
{
List<Observer> observers = new List<Observer>();
string city = "";
string weatherInfo = "";
public void RegisterObserver(Observer observer)
{
observers.Add(observer);
}
public void RemoveObserver(Observer observer)
{
observers.Remove(observer);
}
public void NotifyObservers()
{
foreach (Observer ob in observers)
{
ob.Update(city, weatherInfo);
}
}
private void MeasurementsChanged()
{
NotifyObservers();
}
private void SetMeasurements(string city, string weatherInfo)
{
this.city = city;
this.weatherInfo = weatherInfo;
MeasurementsChanged();
}
public async Task GetWeatherFromWttrAsync(string city)
{
string url = $"https://wttr.in/{city}?T";
using (var client = new HttpClient())
{
try
{
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
SetMeasurements(city, data);
}
catch (HttpRequestException e)
{
Console.WriteLine($"Ошибка при получении данных: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine($"Неизвестная ошибка: {e.Message}");
}
}
}
}
class ConsoleApplication : Observer, DisplayElement
{
string city = "";
string weatherInfo = "";
Subject source;
public ConsoleApplication(Subject source)
{
this.source = source;
this.source.RegisterObserver(this);
}
public void Update(string city, string weatherInfo)
{
this.city = city;
this.weatherInfo = weatherInfo;
Display();
}
public void Display()
{
Console.WriteLine($"Погода в {city}:");
// парсимо температуру повітря на сайті wttr.in
string tempPattern = @"([+-]?\d+)\((\d+)\)\s*°C";
Match tempMatch = Regex.Match(weatherInfo, tempPattern);
if (tempMatch.Success)
{
string temp = tempMatch.Groups[1].Value;
string feels = tempMatch.Groups[2].Value;
Console.WriteLine($"Температура повітря: {temp}°C (відчувається як {feels}°C)");
}
// парсимо вітер
string windPattern = @"([↖↗↘↙→←↓↑])?\s*(\d+(?:-\d+)?)\s*km/h";
Match windMatch = Regex.Match(weatherInfo, windPattern);
if (windMatch.Success)
{
string direction = windMatch.Groups[1].Success ? windMatch.Groups[1].Value : "";
string speed = windMatch.Groups[2].Value;
Console.WriteLine($"Швидкість вітру: {direction} {speed} км/год");
}
// парсимо опади
string precipPattern = @"(\d+\.?\d*)\s*mm";
Match precipMatch = Regex.Match(weatherInfo, precipPattern);
if (precipMatch.Success)
{
string precip = precipMatch.Groups[1].Value;
Console.WriteLine($"Опади: {precip} мм");
}
// парсимо локацію
string locationPattern = @"Location:\s*.+?\s*\[[\d\.]+\s*,[\d\.]+\]";
Match locMatch = Regex.Match(weatherInfo, locationPattern, RegexOptions.Singleline);
if (locMatch.Success)
{
string fullLocation = locMatch.Groups[0].Value.Replace("Location:", "Місцезнаходження:").Trim();
Console.WriteLine(fullLocation);
}
else
{
Console.WriteLine("Місцезнаходження: невідомо");
}
Console.WriteLine();
}
}
class Program
{
public static async Task Main()
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.Title = "Observer";
var weather = new WttrWeatherData();
var app = new ConsoleApplication(weather);
await weather.GetWeatherFromWttrAsync("Одеса");
await weather.GetWeatherFromWttrAsync("Київ");
await weather.GetWeatherFromWttrAsync("Львів");
await weather.GetWeatherFromWttrAsync("Харків");
await weather.GetWeatherFromWttrAsync("Ужгород");
await weather.GetWeatherFromWttrAsync("Миколаїв");
await weather.GetWeatherFromWttrAsync("Барселона");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment