Reputation: 71
I am working on a TCP client application. I would like to share my problem and want to know the
better design approcah for packet consumed in application.
Currently I have designed it as: Consumer class, is activated once it received packet from socket. PacketReceived event is fired by consumer class once it recognized and build up a valid packet.
Now: All my forms (and User controls) which need to consume packet, has subscribed for this event to
get notified and then consume packets for interest by checking speficic packetID.
The bad thing about this design is that, it need to write event subscriptuion code and packetID
verification code on every form (and user control).
Is there any better way to get the job done, please suggest.
I am using C#.net, Framework 3.5.
Thanks. Muhammad Idrees
Upvotes: 0
Views: 108
Reputation: 101140
Sounds like you should use the observer pattern instead and let the consumer decide which packet to send where.
// implement this interface in all forms
public interface IPacketSubscriber
{
void HandlePacket(Packet packet);
}
// like this
public class SomeForm : Form, IPacketSubscriber
{
public SomeForm()
{
// subscribe in some way here.
YouSingleton.Consumer.Subscribe(1, this);
YouSingleton.Consumer.Subscribe(12, this);
YouSingleton.Consumer.Subscribe(25, this);
}
public void HandlePacket(Packet packet)
{
// got packet here
}
}
// Keeps track of all subscribers
public class SubscriberList
{
Dictionary<int, List<IPacketSubscriber>> _subscribers
= new Dictionary<int, List<IPacketSubscriber>>();
public void Subscribe(int packetId, IPacketSubscriber subscriber)
{
List<IPacketSubscriber> subscribers;
if (!_subscribers.TryGetValue(packetId, out subscribers))
{
subscribers = new List<IPacketSubscriber>();
_subscribers.Add(packetId, subscribers);
}
subscribers.Add(subscriber);
}
public void Publish(Packet packet)
{
List<IPacketSubscriber> subscribers;
if (!_subscribers.TryGetValue(packet.FunctionId, out subscribers))
{
subscribers = new List<IPacketSubscriber>();
_subscribers.Add(packet.FunctionId, subscribers);
}
foreach (var subscriber in subscribers)
{
subscriber.HandlePacket(packet);
}
}
}
// changes in the consumer class
// composite pattern & law of demeter, do not expose the subscriberlist
public class Consumer
{
SubscriberList _subscribers = new SubscriberList();
public void Subscribe(int packetId, IPacketSubscriber subscriber)
{
if (subscriber == null) throw new ArgumentNullException("subscriber");
_subscribers.Subscribe(packetId, subscriber);
}
protected void OnReceivedCompletePacket(Packet packet)
{
_subscribers.Publish(packet);
}
}
You can however decoupled things a bit more. The forms doesn't really have to know about the Consumer, only that they can receive events from somewhere. Let's use that knowledge and create another interface:
public interface IPacketDispatcher
{
void Subscribe(int packetId, IPacketSubscriber);
}
And then simply change so that the forms uses the interface instead (depends on how you expose the dispatcher/consumer to the forms).
That little change makes it very easy to test your forms and to change how you receive the packets in the future.
Upvotes: 1