Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Gözlemci tasarım düzeni, abonenin sağlayıcıya kaydolmasını ve sağlayıcıdan bildirim almasını sağlar. Anında iletme tabanlı bildirim gerektiren tüm senaryolar için uygundur. Desen bir sağlayıcı ( konu veya gözlemlenebilir olarak da bilinir) ve sıfır, bir veya daha fazla gözlemci tanımlar. Gözlemciler sağlayıcıya kaydolup önceden tanımlanmış bir koşul, olay veya durum değişikliği gerçekleştiğinde, sağlayıcı bir temsilci çağırarak tüm gözlemcilere otomatik olarak bildirimde bulunur. Bu yöntem çağrısında sağlayıcı, gözlemcilere geçerli durum bilgilerini de sağlayabilir. .NET'te, gözlemci tasarım deseni genel System.IObservable<T> ve System.IObserver<T> arabirimler uygulanarak uygulanır. Genel tür parametresi, bildirim bilgilerini sağlayan türü temsil eder.
Desen ne zaman uygulanır?
Gözlemci tasarım deseni, veri kaynağı (iş mantığı) katmanı ve kullanıcı arabirimi (görüntüleme) katmanı gibi iki farklı bileşen veya uygulama katmanı arasında temiz bir ayrımı desteklediğinden dağıtılmış anında iletme tabanlı bildirimler için uygundur. Sağlayıcı, istemcilerine anlık bilgileri sağlamak için geri aramaları kullandığında bu desen uygulanabilir.
Deseni uygulamak için aşağıdaki ayrıntıları sağlamanız gerekir:
Gözlemcilere bildirim gönderen nesne olan sağlayıcı veya konu. Sağlayıcı, arabirimini uygulayan IObservable<T> bir sınıf veya yapıdır. Sağlayıcı, gözlemciler tarafından çağrılan ve sağlayıcıdan bildirim almak isteyen tek bir yöntem olan IObservable<T>.Subscribe'yi uygulamalıdır.
Bir sağlayıcıdan bildirim alan bir nesne olan gözlemci. Gözlemci, arabirimini uygulayan IObserver<T> bir sınıf veya yapıdır. Gözlemcinin üç yöntem uygulaması gerekir ve bunların tümü sağlayıcı tarafından çağrılır:
- IObserver<T>.OnNext, gözlemciye yeni veya güncel bilgiler sağlar.
- IObserver<T>.OnError, gözlemciye bir hata oluştuğu konusunda bilgi verir.
- IObserver<T>.OnCompleted, sağlayıcının bildirim göndermeyi bitirdiğini gösterir.
Sağlayıcının gözlemcileri izlemesine olanak tanıyan bir mekanizma. Genellikle sağlayıcı, bildirimlere abone olan System.Collections.Generic.List<T> uygulamalarının referanslarını tutmak için IObserver<T> nesne gibi bir kapsayıcı nesne kullanır. Bu amaçla bir depolama kapsayıcısı kullanmak, sağlayıcının sıfırdan sınırsız sayıda gözlemciye kadar işlemesini sağlar. Gözlemcilerin bildirim alma sırası tanımlanmamıştır; sağlayıcı, siparişi belirlemek için herhangi bir yöntemi kullanabilir.
IDisposable Bildirim tamamlandığında sağlayıcının gözlemcileri kaldırmasını sağlayan bir uygulama. Gözlemciler, IDisposable uygulamasına bir başvuru almak için Subscribe yöntemini kullanır, böylece sağlayıcı bildirim göndermeyi tamamlamadan önce abonelikten çıkmak için IDisposable.Dispose yöntemini de çağırabilirler.
Sağlayıcının gözlemcilerine gönderdiği verileri içeren bir nesne. Bu nesnenin türü, IObservable<T> ve IObserver<T> arabirimlerinin genel tür parametresine karşılık gelir. Bu nesne uygulamayla IObservable<T> aynı olsa da, en yaygın olarak ayrı bir tür olur.
Uyarı
Gözlemci tasarım desenini uygulamanın yanı sıra, IObservable<T> ve IObserver<T> arabirimleri kullanılarak oluşturulmuş kütüphaneleri keşfetmek ilginizi çekebilir. Örneğin, .NET (Rx) için Reaktif Uzantılar , zaman uyumsuz programlamayı desteklemek için bir dizi uzantı yönteminden ve LINQ standart dizi işleçlerinden oluşur.
Deseni uygulama
Aşağıdaki örnek, bir havaalanı bagaj talebi bilgi sistemi uygulamak için gözlemci tasarım desenini kullanır. Sınıf BaggageInfo
, gelen uçuşlar ve her uçuşun bagajlarının teslim alınabileceği bantlar hakkında bilgi sağlar. Aşağıdaki örnekte gösterilmiştir.
namespace Observables.Example;
public readonly record struct BaggageInfo(
int FlightNumber,
string From,
int Carousel);
Public Class BaggageInfo
Private flightNo As Integer
Private origin As String
Private location As Integer
Friend Sub New(ByVal flight As Integer, ByVal from As String, ByVal carousel As Integer)
Me.flightNo = flight
Me.origin = from
Me.location = carousel
End Sub
Public ReadOnly Property FlightNumber As Integer
Get
Return Me.flightNo
End Get
End Property
Public ReadOnly Property From As String
Get
Return Me.origin
End Get
End Property
Public ReadOnly Property Carousel As Integer
Get
Return Me.location
End Get
End Property
End Class
BaggageHandler
sınıfı, gelen uçuşlar ve bagaj talep bantları hakkında bilgi almaktan sorumludur. Dahili olarak iki koleksiyon tutar:
-
_observers
: Güncellenmiş bilgileri gözlemleyen istemcilerden oluşan bir koleksiyon. -
_flights
: Uçuşlar ve bunların atanmış kayar bantları koleksiyonu.
Sınıfın BaggageHandler
kaynak kodu aşağıdaki örnekte gösterilmiştir.
namespace Observables.Example;
public sealed class BaggageHandler : IObservable<BaggageInfo>
{
private readonly HashSet<IObserver<BaggageInfo>> _observers = new();
private readonly HashSet<BaggageInfo> _flights = new();
public IDisposable Subscribe(IObserver<BaggageInfo> observer)
{
// Check whether observer is already registered. If not, add it.
if (_observers.Add(observer))
{
// Provide observer with existing data.
foreach (BaggageInfo item in _flights)
{
observer.OnNext(item);
}
}
return new Unsubscriber<BaggageInfo>(_observers, observer);
}
// Called to indicate all baggage is now unloaded.
public void BaggageStatus(int flightNumber) =>
BaggageStatus(flightNumber, string.Empty, 0);
public void BaggageStatus(int flightNumber, string from, int carousel)
{
var info = new BaggageInfo(flightNumber, from, carousel);
// Carousel is assigned, so add new info object to list.
if (carousel > 0 && _flights.Add(info))
{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnNext(info);
}
}
else if (carousel is 0)
{
// Baggage claim for flight is done.
if (_flights.RemoveWhere(
flight => flight.FlightNumber == info.FlightNumber) > 0)
{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnNext(info);
}
}
}
}
public void LastBaggageClaimed()
{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnCompleted();
}
_observers.Clear();
}
}
Public Class BaggageHandler : Implements IObservable(Of BaggageInfo)
Private observers As List(Of IObserver(Of BaggageInfo))
Private flights As List(Of BaggageInfo)
Public Sub New()
observers = New List(Of IObserver(Of BaggageInfo))
flights = New List(Of BaggageInfo)
End Sub
Public Function Subscribe(ByVal observer As IObserver(Of BaggageInfo)) As IDisposable _
Implements IObservable(Of BaggageInfo).Subscribe
' Check whether observer is already registered. If not, add it
If Not observers.Contains(observer) Then
observers.Add(observer)
' Provide observer with existing data.
For Each item In flights
observer.OnNext(item)
Next
End If
Return New Unsubscriber(Of BaggageInfo)(observers, observer)
End Function
' Called to indicate all baggage is now unloaded.
Public Sub BaggageStatus(ByVal flightNo As Integer)
BaggageStatus(flightNo, String.Empty, 0)
End Sub
Public Sub BaggageStatus(ByVal flightNo As Integer, ByVal from As String, ByVal carousel As Integer)
Dim info As New BaggageInfo(flightNo, from, carousel)
' Carousel is assigned, so add new info object to list.
If carousel > 0 And Not flights.Contains(info) Then
flights.Add(info)
For Each observer In observers
observer.OnNext(info)
Next
ElseIf carousel = 0 Then
' Baggage claim for flight is done
Dim flightsToRemove As New List(Of BaggageInfo)
For Each flight In flights
If info.FlightNumber = flight.FlightNumber Then
flightsToRemove.Add(flight)
For Each observer In observers
observer.OnNext(info)
Next
End If
Next
For Each flightToRemove In flightsToRemove
flights.Remove(flightToRemove)
Next
flightsToRemove.Clear()
End If
End Sub
Public Sub LastBaggageClaimed()
For Each observer In observers
observer.OnCompleted()
Next
observers.Clear()
End Sub
End Class
Güncelleştirilmiş bilgileri almak isteyen istemciler yöntemini çağırır BaggageHandler.Subscribe
. İstemci daha önce bildirimlere abone olmadıysa, istemcinin kendi IObserver<T> uygulamasına bir başvuru, _observers
koleksiyonuna eklenir.
Aşırı yüklenmiş BaggageHandler.BaggageStatus
metodu, bir uçuştan gelen bagajların boşaltılmakta olduğunu veya artık boşaltılmadığını belirtmek için çağrılabilir. İlk durumda, metoda bir uçuş numarası, uçuşun başladığı havaalanı ve bagajların boşaltıldığı döner bant iletilir. İkinci durumda, metoda yalnızca bir uçuş numarası aktarılır. Boşaltılan bagajlar için, yönteme aktarılan BaggageInfo
bilgilerin _flights
koleksiyonda mevcut olup olmadığını yöntem denetler. Eğer bu gerçekleşmezse, yöntem bilgileri ekler ve her gözlemcinin OnNext
yöntemini çağırır. Bagajları kaldırılmayan uçuşlar için yöntem, söz konusu uçuşla ilgili bilgilerin koleksiyonda _flights
depolanıp depolanmadığını denetler. Bu durumda, yöntem her gözlemcinin OnNext
yöntemini çağırır ve BaggageInfo
nesnesini _flights
koleksiyonundan kaldırır.
Günün son uçuşu indiğinde ve bagajı işlendiğinde, BaggageHandler.LastBaggageClaimed
yöntemi çağrılır. Bu yöntem, tüm bildirimlerin tamamlandığını belirtmek için her gözlemcinin OnCompleted
yöntemini çağırır ve ardından koleksiyonu temizler _observers
.
Sağlayıcının Subscribe yöntemi, IDisposable yöntemi çağrılmadan önce gözlemcilerin bildirim almayı durdurmasını sağlayan bir OnCompleted uygulama döndürür. Bu Unsubscriber(Of BaggageInfo)
sınıfın kaynak kodu aşağıdaki örnekte gösterilmiştir. Sınıf, BaggageHandler.Subscribe
yönteminde örneklendiğinde, bir _observers
koleksiyonuna ve koleksiyona eklenen gözlemciye bir başvuru geçirilir. Bu referanslar yerel değişkenlere atanır. Nesnenin Dispose
yöntemi çağrıldığında, gözlemcinin koleksiyonda _observers
hala var olup olmadığını denetler ve varsa, gözlemciyi kaldırır.
namespace Observables.Example;
internal sealed class Unsubscriber<BaggageInfo> : IDisposable
{
private readonly ISet<IObserver<BaggageInfo>> _observers;
private readonly IObserver<BaggageInfo> _observer;
internal Unsubscriber(
ISet<IObserver<BaggageInfo>> observers,
IObserver<BaggageInfo> observer) => (_observers, _observer) = (observers, observer);
public void Dispose() => _observers.Remove(_observer);
}
Friend Class Unsubscriber(Of BaggageInfo) : Implements IDisposable
Private _observers As List(Of IObserver(Of BaggageInfo))
Private _observer As IObserver(Of BaggageInfo)
Friend Sub New(ByVal observers As List(Of IObserver(Of BaggageInfo)), ByVal observer As IObserver(Of BaggageInfo))
Me._observers = observers
Me._observer = observer
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If _observers.Contains(_observer) Then
_observers.Remove(_observer)
End If
End Sub
End Class
Aşağıdaki örnek, bagaj talep bilgilerini görüntüleyen bir temel sınıf olan adlı IObserver<T>bir ArrivalsMonitor
uygulama sağlar. Bilgiler, kaynak şehir adıyla alfabetik olarak görüntülenir.
ArrivalsMonitor
yöntemleri overridable
(Visual Basic'te) veya virtual
(C# dilinde) olarak işaretlenir, bu nedenle türetilmiş bir sınıfta geçersiz kılınabilir.
namespace Observables.Example;
public class ArrivalsMonitor : IObserver<BaggageInfo>
{
private readonly string _name;
private readonly List<string> _flights = new();
private readonly string _format = "{0,-20} {1,5} {2, 3}";
private IDisposable? _cancellation;
public ArrivalsMonitor(string name)
{
ArgumentException.ThrowIfNullOrEmpty(name);
_name = name;
}
public virtual void Subscribe(BaggageHandler provider) =>
_cancellation = provider.Subscribe(this);
public virtual void Unsubscribe()
{
_cancellation?.Dispose();
_flights.Clear();
}
public virtual void OnCompleted() => _flights.Clear();
// No implementation needed: Method is not called by the BaggageHandler class.
public virtual void OnError(Exception e)
{
// No implementation.
}
// Update information.
public virtual void OnNext(BaggageInfo info)
{
bool updated = false;
// Flight has unloaded its baggage; remove from the monitor.
if (info.Carousel is 0)
{
string flightNumber = string.Format("{0,5}", info.FlightNumber);
for (int index = _flights.Count - 1; index >= 0; index--)
{
string flightInfo = _flights[index];
if (flightInfo.Substring(21, 5).Equals(flightNumber))
{
updated = true;
_flights.RemoveAt(index);
}
}
}
else
{
// Add flight if it doesn't exist in the collection.
string flightInfo = string.Format(_format, info.From, info.FlightNumber, info.Carousel);
if (_flights.Contains(flightInfo) is false)
{
_flights.Add(flightInfo);
updated = true;
}
}
if (updated)
{
_flights.Sort();
Console.WriteLine($"Arrivals information from {_name}");
foreach (string flightInfo in _flights)
{
Console.WriteLine(flightInfo);
}
Console.WriteLine();
}
}
}
Public Class ArrivalsMonitor : Implements IObserver(Of BaggageInfo)
Private name As String
Private flightInfos As New List(Of String)
Private cancellation As IDisposable
Private fmt As String = "{0,-20} {1,5} {2, 3}"
Public Sub New(ByVal name As String)
If String.IsNullOrEmpty(name) Then Throw New ArgumentNullException("The observer must be assigned a name.")
Me.name = name
End Sub
Public Overridable Sub Subscribe(ByVal provider As BaggageHandler)
cancellation = provider.Subscribe(Me)
End Sub
Public Overridable Sub Unsubscribe()
cancellation.Dispose()
flightInfos.Clear()
End Sub
Public Overridable Sub OnCompleted() Implements System.IObserver(Of BaggageInfo).OnCompleted
flightInfos.Clear()
End Sub
' No implementation needed: Method is not called by the BaggageHandler class.
Public Overridable Sub OnError(ByVal e As System.Exception) Implements System.IObserver(Of BaggageInfo).OnError
' No implementation.
End Sub
' Update information.
Public Overridable Sub OnNext(ByVal info As BaggageInfo) Implements System.IObserver(Of BaggageInfo).OnNext
Dim updated As Boolean = False
' Flight has unloaded its baggage; remove from the monitor.
If info.Carousel = 0 Then
Dim flightsToRemove As New List(Of String)
Dim flightNo As String = String.Format("{0,5}", info.FlightNumber)
For Each flightInfo In flightInfos
If flightInfo.Substring(21, 5).Equals(flightNo) Then
flightsToRemove.Add(flightInfo)
updated = True
End If
Next
For Each flightToRemove In flightsToRemove
flightInfos.Remove(flightToRemove)
Next
flightsToRemove.Clear()
Else
' Add flight if it does not exist in the collection.
Dim flightInfo As String = String.Format(fmt, info.From, info.FlightNumber, info.Carousel)
If Not flightInfos.Contains(flightInfo) Then
flightInfos.Add(flightInfo)
updated = True
End If
End If
If updated Then
flightInfos.Sort()
Console.WriteLine("Arrivals information from {0}", Me.name)
For Each flightInfo In flightInfos
Console.WriteLine(flightInfo)
Next
Console.WriteLine()
End If
End Sub
End Class
ArrivalsMonitor
sınıfı, Subscribe
ve Unsubscribe
yöntemlerini içerir.
Subscribe
yöntemi, IDisposable çağrısının döndürdüğü Subscribe uygulamasını özel bir değişkene kaydetmesi için sınıfa olanak tanır. yöntemi, Unsubscribe
sınıfının sağlayıcının Dispose uygulamasını çağırarak bildirim aboneliğini kaldırmasını sağlar.
ArrivalsMonitor
ayrıca , OnNextve OnError yöntemlerinin OnCompleteduygulamalarını sağlar.
OnNext Yalnızca uygulama önemli miktarda kod içerir. Yöntem, varış uçuşlarının kalkış havaalanları ve bagajlarının bulunduğu döner platformlar hakkında bilgi sağlayan özel, sıralanmış, genel List<T> bir nesneyle çalışır.
BaggageHandler
Sınıf yeni bir uçuş gelişini bildirirse, OnNext yöntem uygulaması bu uçuşla ilgili bilgileri listeye ekler. Sınıf, BaggageHandler
uçuşun bagajının boşaltıldığını bildirirse, OnNext yöntem bu uçuşu listeden kaldırır. Her değişiklik yapıldığında, liste sıralanır ve konsolda görüntülenir.
Aşağıdaki örnek, BaggageHandler
sınıfını ve ArrivalsMonitor
sınıfının iki örneğini oluşturan uygulama giriş noktasını içerir ve gelen uçuşlarla ilgili bilgileri eklemek ve kaldırmak için BaggageHandler.BaggageStatus
yöntemini kullanır. Her durumda, gözlemciler güncelleştirmeleri alır ve bagaj talep bilgilerini doğru bir şekilde görüntüler.
using Observables.Example;
BaggageHandler provider = new();
ArrivalsMonitor observer1 = new("BaggageClaimMonitor1");
ArrivalsMonitor observer2 = new("SecurityExit");
provider.BaggageStatus(712, "Detroit", 3);
observer1.Subscribe(provider);
provider.BaggageStatus(712, "Kalamazoo", 3);
provider.BaggageStatus(400, "New York-Kennedy", 1);
provider.BaggageStatus(712, "Detroit", 3);
observer2.Subscribe(provider);
provider.BaggageStatus(511, "San Francisco", 2);
provider.BaggageStatus(712);
observer2.Unsubscribe();
provider.BaggageStatus(400);
provider.LastBaggageClaimed();
// Sample output:
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
//
// Arrivals information from SecurityExit
// Detroit 712 3
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from BaggageClaimMonitor1
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from SecurityExit
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from BaggageClaimMonitor1
// San Francisco 511 2
Module Example
Public Sub Main()
Dim provider As New BaggageHandler()
Dim observer1 As New ArrivalsMonitor("BaggageClaimMonitor1")
Dim observer2 As New ArrivalsMonitor("SecurityExit")
provider.BaggageStatus(712, "Detroit", 3)
observer1.Subscribe(provider)
provider.BaggageStatus(712, "Kalamazoo", 3)
provider.BaggageStatus(400, "New York-Kennedy", 1)
provider.BaggageStatus(712, "Detroit", 3)
observer2.Subscribe(provider)
provider.BaggageStatus(511, "San Francisco", 2)
provider.BaggageStatus(712)
observer2.Unsubscribe()
provider.BaggageStatus(400)
provider.LastBaggageClaimed()
End Sub
End Module
' The example displays the following output:
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
'
' Arrivals information from SecurityExit
' Detroit 712 3
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from BaggageClaimMonitor1
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from SecurityExit
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from BaggageClaimMonitor1
' San Francisco 511 2
İlgili makaleler
Başlık | Açıklama |
---|---|
Gözlemci Tasarım Deseni En İyi Uygulamalar | Gözlemci tasarım desenini uygulayan uygulamalar geliştirirken benimsenecek en iyi yöntemleri açıklar. |
Nasıl Yapılır: Bir Sağlayıcıyı Uygulama | Sıcaklık izleme uygulaması için bir sağlayıcının adım adım uygulamasını sağlar. |
Nasıl yapılır: Gözlemci Uygulama | Bir sıcaklık izleme uygulaması için bir gözlemcinin adım adım uygulamasını sağlar. |