オブザーバーデザインパターン

マイクロソフトのオブザーバー設計パターンを参考に気象観測システムをオブザーバーパターン(IObserver,IObservableインターフェースを活用)を使って実現テストします

クラス図

コード

namespace P57Observer
{
    class WeatherStation
    {
        public static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();

            CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
            ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
            StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);

            WeatherDataMonitor weatherDataMonitor = new WeatherDataMonitor();
            weatherDataMonitor.Subscribe(currentConditionsDisplay);
            weatherDataMonitor.Subscribe(statisticsDisplay);
            weatherDataMonitor.Subscribe(forecastDisplay);

            weatherDataMonitor.SetMeatuerments(new WeatherData(10, 65, 30.4f));
            weatherDataMonitor.SetMeatuerments(new WeatherData(20, 65, 30.4f));
            weatherDataMonitor.SetMeatuerments(new WeatherData(30, 65, 30.4f));
            weatherDataMonitor.SetMeatuerments(new WeatherData(40, 65, 30.4f));

        }
    }
}
namespace P57Observer
{
    public class WeatherData
    {
        public float temperature;
        public float humidity;
        public float pressure;

        public WeatherData()
        {
        }

        public WeatherData(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
        }
    }
}
using System;
namespace P57Observer
{
    public class WeatherDataMonitor : IObservable<WeatherData>
    {
        List<IObserver<WeatherData>> observers;

        public WeatherDataMonitor()
        {
            observers = new List<IObserver<WeatherData>>();
        }

        public WeatherDataMonitor(List<IObserver<WeatherData>> observers)
        {
            this.observers = observers;
        }

        public void SetMeatuerments(WeatherData weatherData)
        {
            Update(weatherData);
        }

        public IDisposable Subscribe(IObserver<WeatherData> observer)
        {
            if (!observers.Contains(observer))
                observers.Add(observer);

            return new Unsubscriber(observers, observer);
        }

        public void Update(WeatherData weatherData)
        {
            foreach (var observer in observers)
            {
                observer.OnNext(weatherData);
            }
        }
        class Unsubscriber : IDisposable
        {
            private List<IObserver<WeatherData>> _observers;
            private IObserver<WeatherData> _observer;

            public Unsubscriber(List<IObserver<WeatherData>> observers, IObserver<WeatherData> observer)
            {
                this._observers = observers;
                this._observer = observer;
            }

            public void Dispose()
            {
                if (!(_observer == null)) _observers.Remove(_observer);
            }
        }
    }
}
namespace P57Observer
{
    internal class CurrentConditionsDisplay : IObserver<WeatherData>, IDisplayElement
    {
        float temperature;
        float humidity;
        private WeatherData weatherData;
        private IDisposable? unsubscriber;


        public CurrentConditionsDisplay(WeatherData weatherData)
        {
            this.weatherData = weatherData;
        }

        public void Display()
        {
            Console.WriteLine($"現在の気象状況:温度{temperature}度(摂氏) 湿度{humidity}%");
        }

        public void OnCompleted()
        {
            Console.WriteLine("追加の温度データは送信されません");
        }

        public void OnError(Exception error)
        {
            // Do nothing.
        }

        public void OnNext(WeatherData weatherData)
        {
            temperature = weatherData.temperature;
            humidity = weatherData.humidity;

            Display();
        }

        public virtual void Unsubscribe()
        {
            unsubscriber?.Dispose();
        }

        public virtual void Subscribe(IObservable<WeatherData> provider)
        {
            unsubscriber = provider.Subscribe(this);
        }
    }
}
namespace P57Observer
{
    internal class ForecastDisplay : IObserver<WeatherData>, IDisplayElement
    {
        private WeatherData weatherData;
        private IDisposable? unsubscriber;


        public ForecastDisplay(WeatherData weatherData)
        {
            this.weatherData = weatherData;
        }

        public void Display()
        {
            if (weatherData.temperature > 40)
            {
                Console.WriteLine($"極暑になるでしょう");
            }
            else if (weatherData.temperature > 30)
            {
                Console.WriteLine($"猛暑になるでしょう");
            }
            else if (weatherData.temperature > 20)
            {
                Console.WriteLine($"暑くなるでしょう");
            }
            else if (weatherData.temperature > 10)
            {
                Console.WriteLine($"過ごしやすいでしょう");
            }
            else if (weatherData.temperature > 0)
            {
                Console.WriteLine($"肌寒いでしょう");
            }
        }

        public void OnCompleted()
        {
            Console.WriteLine("追加の温度データは送信されません");
        }

        public void OnError(Exception error)
        {
            // Do nothing.
        }

        public void OnNext(WeatherData weatherData)
        {
            this.weatherData = weatherData;

            Display();
        }

        public virtual void Unsubscribe()
        {
            unsubscriber?.Dispose();
        }

        public virtual void Subscribe(IObservable<WeatherData> provider)
        {
            unsubscriber = provider.Subscribe(this);
        }
    }
}
namespace P57Observer
{
    using System.Linq;

    internal class StatisticsDisplay : IObserver<WeatherData>, IDisplayElement
    {
        private WeatherData weatherData;
        private IDisposable? unsubscriber;


        List<WeatherData> weatherDatas = new List<WeatherData>();

        public StatisticsDisplay(WeatherData weatherData)
        {
            this.weatherData = weatherData;
        }

        public void Display()
        {
            var average = weatherDatas.Average(data => data.temperature);
            var min = weatherDatas.Min(data => data.temperature);
            var max = weatherDatas.Max(data => data.temperature);

            Console.WriteLine($"平均/最高/最低気温:{average}/{max}/{min}");
        }

        public void OnCompleted()
        {
            Console.WriteLine("追加の温度データは送信されません");
        }

        public void OnError(Exception error)
        {
            // Do nothing.
        }

        public void OnNext(WeatherData weatherData)
        {
            weatherDatas.Add(weatherData);

            Display();
        }

        public virtual void Unsubscribe()
        {
            unsubscriber?.Dispose();
        }

        public virtual void Subscribe(IObservable<WeatherData> provider)
        {
            unsubscriber = provider.Subscribe(this);
        }
    }
}
namespace P57Observer
{
    internal interface IDisplayElement
    {
        void Display();
    }
}

結果

現在の気象状況:温度10度(摂氏) 湿度65%
平均/最高/最低気温:10/10/10
肌寒いでしょう
現在の気象状況:温度20度(摂氏) 湿度65%
平均/最高/最低気温:15/20/10
過ごしやすいでしょう
現在の気象状況:温度30度(摂氏) 湿度65%
平均/最高/最低気温:20/30/10
暑くなるでしょう
現在の気象状況:温度40度(摂氏) 湿度65%
平均/最高/最低気温:25/40/10
猛暑になるでしょう

参考