Last active
December 28, 2025 11:44
-
-
Save sunmeat/ec2194a8c89833d9a89ac8ed5f8fb426 to your computer and use it in GitHub Desktop.
pattern observer - 4 examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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