Trong bài viết này, bạn sẽ tìm hiểu về Mẫu thiết kế Người quan sát trong .NET C# với một số cải tiến. Định nghĩa mẫu thiết kế của người quan sát là một trong những mẫu thiết kế quan trọng nhất và thường được sử dụng. Mẫu thiết kế Người quan sát Trước tiên, hãy kiểm tra định nghĩa chính thức của . Mẫu thiết kế Người quan sát Theo : tài liệu của Microsoft Mẫu thiết kế người quan sát cho phép người đăng ký đăng ký và nhận thông báo từ nhà cung cấp. Nó phù hợp với mọi tình huống yêu cầu thông báo dựa trên đẩy. Mẫu xác định một nhà cung cấp (còn được gọi là chủ thể hoặc có thể quan sát được) và không, một hoặc nhiều người quan sát. Người quan sát đăng ký với nhà cung cấp và bất cứ khi nào xảy ra điều kiện, sự kiện hoặc thay đổi trạng thái được xác định trước, nhà cung cấp sẽ tự động thông báo cho tất cả người quan sát bằng cách gọi một trong các phương thức của họ. Trong cuộc gọi phương thức này, nhà cung cấp cũng có thể cung cấp thông tin trạng thái hiện tại cho người quan sát. Trong .NET, mẫu thiết kế trình quan sát được áp dụng bằng cách triển khai các giao diện và chung. Tham số loại chung đại diện cho loại cung cấp thông tin thông báo. System.IObservable<T> System.IObserver<T> Như vậy, từ định nghĩa trên, chúng ta có thể hiểu như sau: Chúng tôi có hai bên hoặc mô-đun. Mô-đun có một số luồng thông tin để cung cấp. Mô-đun này được gọi là (vì nó cung cấp thông tin) hoặc (vì nó đưa thông tin ra thế giới bên ngoài) hoặc (vì nó có thể được quan sát bởi thế giới bên ngoài). Nhà cung cấp Chủ thể Có thể quan sát được Mô-đun quan tâm đến luồng thông tin đến từ một nơi khác. Mô-đun này được gọi là (vì nó quan sát thông tin). Người quan sát Ưu điểm của mẫu thiết kế quan sát Như chúng ta đã biết, hình thành mối quan hệ giữa các mô-đun và . Điều làm cho trở nên độc đáo là khi sử dụng nó, bạn có thể đạt được điều này mà không cần có mối quan hệ liên kết chặt chẽ. Mẫu thiết kế Người quan sát Có thể quan sát Người quan sát Mẫu thiết kế Người quan sát Phân tích cách thức hoạt động của mô hình, bạn sẽ tìm thấy những điều sau: biết thông tin tối thiểu cần thiết về . Observable Observer biết thông tin tối thiểu cần thiết về . Người quan sát Người quan sát Ngay cả kiến thức lẫn nhau cũng đạt được thông qua trừu tượng hóa, không phải triển khai cụ thể. Cuối cùng, cả hai mô-đun đều có thể thực hiện công việc của chúng và chỉ công việc của chúng. Trừu tượng được sử dụng Đây là được sử dụng để triển khai trong . những phần trừu tượng Mẫu thiết kế Người quan sát .NET C# IObservable<ra T> Đây là một giao diện đại diện cho bất kỳ nào. Nếu bạn muốn biết thêm về Variance trong .NET, bạn có thể xem bài viết . Covariant Observable Hiệp phương sai và Chống phương sai trong .NET C# Các thành viên được định nghĩa trong giao diện này là: public IDisposable Subscribe (IObserver<out T> observer); Phương thức nên được gọi để thông báo cho rằng một số quan tâm đến luồng thông tin của nó. Subscribe Observable Observer Phương thức trả về một đối tượng cài đặt giao diện . Sau đó, đối tượng này có thể được sử dụng để hủy đăng ký khỏi luồng thông tin do cung cấp. Khi điều này được thực hiện, sẽ không được thông báo về bất kỳ cập nhật nào đối với luồng thông tin. Subscribe IDisposable Người quan sát Người quan sát Người quan sát IObserver<trong T> Đây là giao diện đại diện cho bất kỳ . Nếu bạn muốn biết thêm về Variance trong .NET, bạn có thể xem bài viết . Contravariant Người quan sát nào Hiệp phương sai và Chống phương sai trong .NET C# Các thành viên được định nghĩa trong giao diện này là: public void OnCompleted (); public void OnError (Exception error); public void OnNext (T value); Phương thức nên được gọi bởi để thông báo cho rằng luồng thông tin đã hoàn thành và không nên mong đợi thêm bất kỳ thông tin nào. OnCompleted Observable Người quan sát Người quan sát Phương thức nên được gọi bởi để thông báo cho rằng đã xảy ra lỗi. OnError Observable Người quan sát Phương thức nên được gọi bởi để thông báo cho rằng một phần thông tin mới đã sẵn sàng và đang được thêm vào luồng. OnNext Observable Người quan sát Triển khai của Microsoft Bây giờ, hãy xem cách khuyến nghị triển khai trong C#. Sau đó, tôi sẽ cho bạn thấy một số cải tiến nhỏ do chính tôi thực hiện. Microsoft Mẫu thiết kế Người quan sát Chúng tôi sẽ xây dựng một đơn giản. Trong ứng dụng này, chúng ta sẽ có mô-đun (Có thể quan sát, Nhà cung cấp, Chủ đề) và mô-đun (Người quan sát). Ứng dụng bảng điều khiển dự báo thời tiết WeatherForecast WeatherForecastObserver Vì vậy, hãy bắt đầu xem xét việc thực hiện. Thông tin thời tiết namespace Observable { public class WeatherInfo { internal WeatherInfo(double temperature) { Temperature = temperature; } public double Temperature { get; } } } Đây là thực thể đại diện cho phần thông tin được truyền trong luồng thông tin. Dự báo thời tiết using System; using System.Collections.Generic; namespace Observable { public class WeatherForecast : IObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IDisposable Subscribe(IObserver<WeatherInfo> observer) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } return new WeatherForecastUnsubscriber(m_Observers, observer); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } } Những gì chúng ta có thể nhận thấy ở đây: Lớp đang triển khai . WeatherForecast IObservable<WeatherInfo> Khi triển khai phương thức , chúng tôi kiểm tra xem thông qua trong Người quan sát đã được đăng ký trước đó hay chưa. Nếu không, chúng tôi thêm nó vào danh sách người quan sát cục bộ. Sau đó, chúng tôi lặp lại tất cả các mục mà chúng tôi có trong danh sách cục bộ từng cái một và thông báo cho Người quan sát về điều đó bằng cách gọi phương thức của Người quan sát. Subscribe m_Observers WeatherInfo m_WeatherInfoList OnNext Cuối cùng, chúng tôi trả về một phiên bản mới của lớp sẽ được Người quan sát sử dụng để hủy đăng ký luồng thông tin. WeatherForecastUnsubscriber Phương thức được xác định để mô-đun chính có thể đăng ký mới. Trong thế giới thực, điều này có thể được thay thế bằng được lên lịch nội bộ hoặc trình nghe đối với hoặc thứ gì đó khác sẽ hoạt động như một nguồn thông tin. RegisterWeatherInfo WeatherInfo lệnh gọi API Trung tâm SignalR Người hủy đăng ký<T> using System; using System.Collections.Generic; namespace Observable { public class Unsubscriber<T> : IDisposable { private readonly List<IObserver<T>> m_Observers; private readonly IObserver<T> m_Observer; private bool m_IsDisposed; public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer) { m_Observers = observers; m_Observer = observer; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing && m_Observers.Contains(m_Observer)) { m_Observers.Remove(m_Observer); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } } Những gì chúng ta có thể nhận thấy ở đây: Đây là lớp cơ sở cho bất kỳ Người không đăng ký nào. Nó triển khai bằng cách áp dụng . IDisposable Disposable Design Pattern Thông qua hàm tạo, nó nhận danh sách đầy đủ của Người quan sát và Người quan sát mà nó được tạo. Trong khi xử lý, nó sẽ kiểm tra xem Người quan sát đã tồn tại trong danh sách Người quan sát đầy đủ chưa. Nếu có, nó sẽ xóa nó khỏi danh sách. WeatherForecastHủy đăng ký using System; using System.Collections.Generic; namespace Observable { public class WeatherForecastUnsubscriber : Unsubscriber<WeatherInfo> { public WeatherForecastUnsubscriber( List<IObserver<WeatherInfo>> observers, IObserver<WeatherInfo> observer) : base(observers, observer) { } } } Những gì chúng ta có thể nhận thấy ở đây: Điều này kế thừa từ lớp . Unsubscriber<T> Không có xử lý đặc biệt đang xảy ra. WeatherForecastNgười quan sát using System; namespace Observable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } } Những gì chúng ta có thể nhận thấy ở đây: Lớp đang triển khai . WeatherForecastObserver IObserver<WeatherInfo> Trên phương thức , chúng tôi đang ghi nhiệt độ vào bàn điều khiển. OnNext Trên phương thức , chúng ta đang viết “Completed” vào bảng điều khiển. OnCompleted Trên phương thức , chúng tôi đang ghi “Lỗi” vào bảng điều khiển. OnError Chúng tôi đã xác định phương thức để cho phép mô-đun chính kích hoạt quá trình đăng ký. Đối tượng hủy đăng ký được trả về được lưu nội bộ để sử dụng trong trường hợp hủy đăng ký. void Subscribe(WeatherForecast provider) Sử dụng cùng một khái niệm, phương thức được định nghĩa và nó sử dụng đối tượng un-subscriber được lưu nội bộ. void Unsubscribe() Chương trình using System; namespace Observable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } } Những gì chúng ta có thể nhận thấy ở đây: Chúng tôi đã tạo một phiên bản của nhà cung cấp. Sau đó đăng ký 3 mẩu thông tin. Cho đến thời điểm này, không có gì được ghi vào bảng điều khiển vì không có người quan sát nào được xác định. Sau đó, tạo một thể hiện của người quan sát. Sau đó đăng ký người quan sát vào luồng. Tại thời điểm này, chúng ta sẽ tìm thấy 3 nhiệt độ đã ghi trong bảng điều khiển. Điều này là do khi người quan sát đăng ký, nó sẽ được thông báo về thông tin đã có và trong trường hợp của chúng tôi, chúng là 3 mẩu thông tin. Sau đó, chúng tôi đăng ký 2 mẩu thông tin. Vì vậy, chúng tôi nhận được thêm 2 tin nhắn được ghi vào bảng điều khiển. Sau đó, chúng tôi hủy đăng ký. Sau đó, chúng tôi đăng ký 1 mẩu thông tin. Tuy nhiên, phần thông tin này sẽ không được ghi vào bảng điều khiển vì người quan sát đã hủy đăng ký. Sau đó, người quan sát đăng ký lại. Sau đó, chúng tôi đăng ký 1 mẩu thông tin. Vì vậy, phần thông tin này được ghi vào bảng điều khiển. Cuối cùng, chạy cái này sẽ có kết quả như sau: Triển khai mở rộng của tôi Khi kiểm tra quá trình triển khai của Microsoft, tôi thấy có một số lo ngại. Do đó, tôi quyết định thực hiện một số thay đổi nhỏ. IExtendedObservable<ra T> using System; using System.Collections.Generic; namespace ExtendedObservable { public interface IExtendedObservable<out T> : IObservable<T> { IReadOnlyCollection<T> Snapshot { get; } IDisposable Subscribe(IObserver<T> observer, bool withHistory); } } Những gì chúng ta có thể nhận thấy ở đây: Giao diện mở rộng giao diện . IExtendedObservable<out T> IObservable<T> Nó là . Nếu bạn muốn biết thêm về điều này, bạn có thể kiểm tra bài viết . Hiệp biến Hiệp phương sai và Chống phương sai trong .NET C# Chúng tôi đã xác định thuộc tính để cho phép các mô-đun khác nhận danh sách tức thời các mục nhập thông tin đã tồn tại mà không cần phải đăng ký. IReadOnlyCollection<T> Snapshot Chúng tôi cũng đã xác định phương thức với tham số bổ sung để Người quan sát có thể quyết định xem nó có muốn nhận thông báo về các mục nhập thông tin hiện có hay không tại thời điểm đăng ký. IDisposable Subscribe(IObserver<T> observer, bool withHistory) bool withHistory hủy đăng ký using System; namespace ExtendedObservable { public class Unsubscriber : IDisposable { private readonly Action m_UnsubscribeAction; private bool m_IsDisposed; public Unsubscriber(Action unsubscribeAction) { m_UnsubscribeAction = unsubscribeAction; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing) { m_UnsubscribeAction(); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } } Những gì chúng ta có thể nhận thấy ở đây: Bây giờ, lớp không phải là chung chung. Unsubscriber Điều này là do không cần biết thêm về loại thực thể thông tin. Thay vì có quyền truy cập vào danh sách đầy đủ của Người quan sát và Người quan sát mà nó được tạo, nó chỉ thông báo cho Người quan sát khi nó được xử lý và Người quan sát có thể tự xử lý quá trình hủy đăng ký. Bằng cách này, nó đang làm ít hơn trước và nó chỉ đang làm công việc của mình. WeatherForecastHủy đăng ký using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecastUnsubscriber : Unsubscriber { public WeatherForecastUnsubscriber( Action unsubscribeAction) : base(unsubscribeAction) { } } } Những gì chúng ta có thể nhận thấy ở đây: Chúng tôi đã xóa phần khỏi . <T> Unsubscriber<T> Và bây giờ, hàm tạo thực hiện một sẽ được gọi trong trường hợp xử lý. Action Dự báo thời tiết using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecast : IExtendedObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IReadOnlyCollection<WeatherInfo> Snapshot => m_WeatherInfoList; public IDisposable Subscribe(IObserver<WeatherInfo> observer) { return Subscribe(observer, false); } public IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); if (withHistory) { foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } } return new WeatherForecastUnsubscriber( () => { if (m_Observers.Contains(observer)) { m_Observers.Remove(observer); } }); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } } Những gì chúng ta có thể nhận thấy ở đây: Nó gần như giống nhau ngoại trừ thuộc tính trả về danh sách nội bộ nhưng dưới dạng . IReadOnlyCollection<WeatherInfo> Snapshot m_WeatherInfoList IReadOnlyCollection Và phương thức sử dụng tham số . IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory) withHistory WeatherForecastNgười quan sát using System; namespace ExtendedObservable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this, true); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } } Những gì chúng ta có thể nhận thấy ở đây là nó gần như giống nhau ngoại trừ hiện quyết định xem có nên với lịch sử hay không. Subscribe(WeatherForecast provider) Subscribe Chương trình using System; namespace ExtendedObservable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } } Nó cũng giống như trước đây. Cuối cùng, chạy cái này sẽ có kết quả giống như trước đây: Cái gì tiếp theo Bây giờ, bạn đã biết những kiến thức cơ bản về trong .NET C#. Tuy nhiên, đây không phải là kết thúc của câu chuyện. Observer Design Pattern Có các thư viện được xây dựng trên các giao diện và cung cấp nhiều tính năng và khả năng thú vị hơn mà bạn có thể thấy hữu ích. IObservable<T> IObserver<T> Trên các thư viện này là thư viện. Nó bao gồm một tập hợp các phương thức mở rộng và các toán tử tuần tự chuẩn LINQ để hỗ trợ lập trình không đồng bộ. Tiện ích mở rộng phản ứng cho .NET (Rx) Do đó, tôi khuyến khích bạn khám phá các thư viện này và dùng thử. Tôi chắc chắn rằng bạn muốn một số trong số họ. Cũng được xuất bản ở đây.