We have a class which manages many queues that store data. I want a user to get notified when new data is added to each of these queues. I'd like to use the observer pattern using delegate and events. Normally for a single event and source, we'd do:
public delegate void NewDataAddedDelegate();
public event NewDataAddedDelegate NewDataAdded;
and for the observer:
qManager.NewDataAdded += new qManager.NewDataAddedDelegate(getNewDataFunc);
But in this case, we have, say, 10 queues, each of which can receive data arbitrarily. So we'd like the observer functions to subscribe to an individual queue. We thought we could do:
public delegate void NewDataAddedDelega开发者_JS百科te();
public event NewDataAddedDelegate [] NewDataAdded; // can't do this
and in the constructor of qManager:
NewDataAdded = new NewDataAddedDelegate[numberOfQueues];
and in the observer:
qManager.NewDataAdded[0] += new qManager.NewDataAddedDelegate(getNewDataFunc0);
qManager.NewDataAdded[1] += new qManager.NewDataAddedDelegate(getNewDataFunc1);
but no go, since the event is expected to be a delegate type, not an array of delegates type.
Any ideas on how to approach this problem?
No, events don't work like that. Options:
Create another type which exposes the event, and have an array or collection of that type:
// Preferably *don't* just expose an array... public TypeWithEvent[] Queues { get { ... } } // Subscription: qManager.Queues[i].NewDataAdded += ...
Alternatively, don't use events, and just have a method:
private NewDataAddededDelegate[] newDataAdded; public void SubscribeNewDataAddedHandler(int queue, NewDataAddedDelegate handler) { newDataAdded[queue] += handler; } // Subscription qManager.SubscribeNewDataAddedHandler(0, ...);
Personally it sounds to me like each queue should really be its own object though... make the queue manager exposes a collection of queues, each of which can be subscribed to individually. (i.e. take the first approach.) Otherwise your queue manager is really doing too much work.
There are two approaches you might take there; the first is to have:
private NewDataAddedDelegate[] queues; // init not shown
public void Subscribe(int index, NewDataAddedDelegate handler) {
queues[index] += handler;
}
and use
obj.Subscribe(index, ...);
but you may want to think about synchronization etc around the subscription. A second approach is to create a wrapper class that has the event - then you can use the compiler's synchronization, which is good in C# 4.0:
public class SomeQueue {
public event NewDataAddedDelegate NewDataAdded;
}
and then expose those perhaps via an indexer, so you have
obj.Queues[index].NewDataAdded += ...
Personally, I expect the first is easier. It is only the synchronization that might be a nuisance. I do this in some pub-sub code, and IIRC I just lock
during the subscribe.
You need to rethink this by actually applying the observer pattern rather that working on the basis of a fuzzy idea of a pattern.
Define your IObserver and ISubject interfaces and try to understand what's the observer and what are the subjects. In your case sounds like the queues are the subjects, not sure what the observers would be in your domain model.
Once you do this, things will be easier to figure out, and it's simply a matter of implementing the methods declared by your interfaces, for example your subjects (queues) will just call notify (and raise an event if you wanna use delegates) when something happens (item added to the queue).
Hope this helps.
Here is the working code in C#.
QueueManger exposes event NewDataAddedEvent which can be subscribed by one or more observers. Queue calls NewDataAdded() method on QueueManager on data change. QueueManager notifies if there are any subscribers with the parameter Queue. I hope this addresses your question.
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
QueueManager queueManager = new QueueManager();
Observer observer = new Observer(queueManager);
Queue queue1 = queueManager.AddQueue();
Queue queue2 = queueManager.AddQueue();
queue1.OnNewDataAdd();
queue2.OnNewDataAdd();
Console.ReadLine();
}
delegate void NewDataAddedDelegate(Queue queue);
class Queue
{
QueueManager queueManager;
public string id;
public Queue(string id, QueueManager queueManager)
{
this.id = id;
this.queueManager = queueManager;
}
public void OnNewDataAdd()
{
this.queueManager.NewDataAdded(this);
}
}
class QueueManager
{
List<Queue> queues = new List<Queue>();
public Queue AddQueue()
{
Queue queue = new Queue((queues.Count + 1).ToString(), this);
this.queues.Add(queue);
return queue;
}
public event NewDataAddedDelegate NewDataAddedEvent;
public void NewDataAdded(Queue queue)
{
if (NewDataAddedEvent != null)
NewDataAddedEvent(queue);
}
}
class Observer
{
public Observer(QueueManager queueManager)
{
queueManager.NewDataAddedEvent += new NewDataAddedDelegate(queue_NewDataAdded);
}
void queue_NewDataAdded(Queue queue)
{
Console.WriteLine("Notification to the observer from queue {0}", queue.id);
}
}
}
}
Perhaps you could use the Event Aggregator pattern.
Not that you would have to code less, but it could create more clean/maintainable code.
精彩评论