C#
を使用します。
注目を集める画像:
したがって、オブジェクトのセットを追跡するシステムの他の部分を提供するサービスで構成されるシステムを想像してください。 シミュレートされたオブジェクトまたはその他の類似したオブジェクトのリストを提供するシミュレーションサービスにすることができます。
システムによって生成および破棄されるオブジェクト:
public interface IObject { }
オブジェクトへのアクセスを提供するサービス:
public delegate void ServiceChangedHandle(IService sender, IObject item, bool injected); public interface IService { IEnumerable<IObject> Items { get; } event ServiceChangedHandle OnServiceChanged; }
オブジェクトを操作する必要があるシステムは、新しいオブジェクトの出現と現在のオブジェクトの消失を追跡するためにイベントにサブスクライブされます。
典型的なリスナーの例:
public class Listener { public void Initialise() { foreach (var item in service.Items) RegisterItem(item); service.OnServiceChanged += OnServiceChanged; } public void Shutdown() { service.OnServiceChanged -= OnServiceChanged; foreach (var item in service.Items) UnregisterItem(item); } private void OnServiceChanged(IService sender, IObject item, bool injected) { if (injected) RegisterItem(item); else UnregisterItem(item); } private void RegisterItem(IObject item) { ... } private void UnregisterItem(IObject item) { ... } private IService service; }
リスナーがアクティブ化されると、サービス内にすでに存在するすべてのオブジェクトを処理し、その後、オブジェクトのリストの変更をサブスクライブします。 リスナーの作業が完了したら、逆のアクションを実行する必要があります。
マルチスレッドプログラミングでは、ポーリングとサブスクライブの間でオブジェクトのリストが変わる可能性があるため、事態は複雑になります。 サービスは同期オブジェクトを提供する必要があり、リスナーはリストのクロールとサブスクリプションの間のリストの変更をブロックします。
マルチスレッドをサポートするサービス:
public interface IService { ... // object SyncRoot { get; } }
マルチスレッドサービスをサポートするリスナー(内部同期は省略):
public class Listener { public void Initialise() { // lock (service.SyncRoot) { foreach (var item in service.Items) RegisterItem(item); service.OnServiceChanged += OnServiceChanged; } } public void Shutdown() { // lock (service.SyncRoot) { service.OnServiceChanged -= OnServiceChanged; foreach (var item in service.Items) UnregisterItem(item); } } ... }
サブスクリプションおよびサブスクリプション解除時にサービスにオブジェクトが存在しないことを保証する場合、サブスクリプションシステムを少し簡素化できます。 特にサービスの出現時間が定義されていないシステムでは、このような保証を与えることは困難です。
しかし、各加入者に対してこの保証をエミュレートできます。これがパターンの本質です。 サブスクライブするとき、サービスは既存のすべてのオブジェクトのオブジェクト出現イベントを強制的に送信し、サブスクライブ解除するとき、消滅イベントを送信します。 同時に、リスナーは単純化されており、マルチスレッド版とシングルスレッド版では同じように見えます。
マルチスレッドおよびシングルスレッドサービスオプションのサブスクライバー(内部同期は省略):
public class Listener { public void Initialise() { service.OnServiceChanged += OnServiceChanged; } public void Shutdown() { service.OnServiceChanged -= OnServiceChanged; } ... }
シングルスレッドバージョンのサービス実装:
public class Service : IService { ... public event ServiceChangedHandle OnServiceChanged { add { // foreach (var item in items) value(this, item, true); // eventHandle += value; } remove { // eventHandle -= value; // foreach (var item in items) value(this, item, false); } } private ServiceChangedHandle eventHandle; private List<IObject> items = new List<IObject>(); }
他のパターンと同様に、このバージョンのリスナーには長所、短所、スコープがあります。
長所:
- 購読者は単純化され、単純な購読と購読解除のみ
- マルチスレッド版とシングルスレッド版の両方で同じコード
短所:
- 使用するときは、オブジェクトが2回処理されないように、サービスのこの機能を知る必要があります
マイナスとプラスから、パターンの範囲を区別できます。
- 少数のサービスと多数の加入者の存在
- 会社の内部製品または純粋に個人的なもので、そのような行動を公開することは危険です
- 設計と開発の厳格な規律。 各開発者は、この動作を認識し、このパターンが特に使用されている場所を知っている必要があります。
Update1
いくつかの説明:
「オブザーバー」パターンは既に鋭く実装されており、オブザーバーとオブザーバーの代わりに、イベントとサブスクライバーがあります。
VIPは「1」という言葉の同義語ではなく、「特別」という言葉の同義語です。 この場合、監視対象オブジェクトは個々の監視者ごとに異なる動作をするため、すべてのサブスクライバは特別です。 つまり、オブザーバがスキップしたり待機したりできないイベントを生成します。
ご清聴ありがとうございました!