Skip to main content
gamedev.net
🔒 Locked

Design feedback

Started by antareus 2 replies 950+ views
Original Post
antareus
antareus
I am designing an open source instant messaging client. I know not to just jump in and start coding. It is completely plugin-based within reason. However this is my first real independent project and its hard to know when to throw in the towel as far as inelegance/compromises that inevitably arise, esp. since I''ve been reading a lot of ''academia''-type OOP books. Big question: I have a base Plugin class that is abstract. In addition, I inherit from that in the ProtocolPlugin class. ProtocolPlugins represent a single network connection to an instant messaging server, handling the encoding/decoding. Although, it seems like ProtocolPlugin does too much, having a network connection AND doing encoding/decoding. Perhaps I should make a Connection class that is separate? Littler questions: I package everything together in an Engine object that handles the coupling between the different managers. Is this how it''s normally done? Plugins need to access portions of the engine. Should I package them up as references in a Link object and send it down when the object is created? My goals are code that is readable, understandable, and flexible enough to allow future revisions. I appreciate any comments you might have, I feel like I''m thrashing around in the dark at times.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
PaleRaider
PaleRaider
antareus this type of networking problem comes up allot. I am in agreement with you in that your Plugin, by itself might be doing to much. In the book "Patterns of Software Architecture 2" (POSA2) they outline a pattern called Acceptor/Connector that deals with establishing a network connection to a remote server and interacting with it. This idea would solve your problem ideally!

The general idea is you have two abstract classes, Connector and Acceptor. The Connector is, obviously enough, in charge of establishing a connection to the remote system, it doesn''t handle the actual conversation with the remote server, only the initialization. The Acceptor is responsible for carrying out the encoding and decoding of the data. In your instance you could break down the Acceptor even further so you have an Encoder and Decoder abstract base class.

To extend this concept a little further and allow you to decouple the engine from the plug-in instances I would recommend an abstract factory that creates your "plug in package". What this factory should return is an instance of Plugin class, which of course it is an abstract base class. This Plugin should contain your Connector/(Encoder/Decoder) classes, and provides the interface to the client for creating connections and such. I wouldn''t expose the classes contained in the Plugin to it clients. Instead provide methods in the Plugin that allow the client to send network messages and so forth. As far as returning messages to the client/engine I would create sometype of Handler class that is registered with the Plugin at startup, this Handler has methods for displying data, which the Engine itself can implement or some GUI element controlled by the Engine. The choice is yours as to putting the Handler in the Decoder itself, or to have the Plugin be responsible for being a proxy between the Decoder to the Hanlder.

Wow looking back at that I think I got carried away and may not have provided you with any obvious answers so I will try to relate the above to your three questions.

1. You stated you had one Engine that handles the coupling between the different managers. The Engine doesn''t need to know about the different Plugin instances, it should only request an instance, or multiple instance from an entity that can create the correct Plugin. This would be the abstract factory "PluginFactory", it could have a method such as "Plugin* GetPlugin(const char* type)". This makes the Engine a consumer of the Plugin only. As for how it is normally done is any ones guess .
2. Plugins need to access parts of the Engine. Don''t couple the Plugin or its contained components to the Engine. Instead create Proxy classes such as the Handler, only let the Plugin deal with the Handlers. That way the Plugin isn''t dependent upon the Engine. I am assuming that the Handler is for displaying data, but you could create another abstract class that provides a common interface to operational support methods as well.
3. Trying to make your system understandable and flexible is great goal to have. I would keep the creation of the Plugins out of the Engine itself, and wouldn''t have any of the networking code be dependent/coupled with the higher level classes that contain it, i.e. Engine.

I don''t know if this will help you any but good luck.
antareus
antareus
Had some revelations lately:
* Instantiating a Plugin/ProtocolPlugin multiple times makes absolutely no sense, this semantically implies I'm doing a ::LoadLibrary on the module again.

* ProtocolPlugins are doing too much for their own good. Doesn't make sense to issue commands (such as Disconnect) to the plugin in the code. I'm going to change them to be factories that create derived Connection objects. Within these, the responsibilities are split up.

Secondly, the notion of a single PluginManager handling everything is a bit iffy when the whole engine revolves around different types of plugins. Perhaps splitting them up into Input, Processing, and Output as the three types and putting them at the Engine level.

I can sidestep DLL function linking woes by using the static global technique (static instance of plugin in cpp file of plugin , which registers itself with a static function of the corresponding manager).

quote:
Original post by PaleRaider
In the book "Patterns of Software Architecture 2" (POSA2) they outline a pattern called Acceptor/Connector that deals with establishing a network connection to a remote server and interacting with it. This idea would solve your problem ideally!

The general idea is you have two abstract classes, Connector and Acceptor. The Connector is, obviously enough, in charge of establishing a connection to the remote system, it doesn't handle the actual conversation with the remote server, only the initialization. The Acceptor is responsible for carrying out the encoding and decoding of the data. In your instance you could break down the Acceptor even further so you have an Encoder and Decoder abstract base class.


I have skimmed this pattern at the bookstore and it seems really specific, I'm not sure if I can apply it, it looks to be overkill, but then again I thought some of GoF was.

quote:

Don't couple the Plugin or its contained components to the Engine. Instead create Proxy classes such as the Handler, only let the Plugin deal with the Handlers. That way the Plugin isn't dependent upon the Engine. I am assuming that the Handler is for displaying data, but you could create another abstract class that provides a common interface to operational support methods as well.

I assume you are referring to something like the pImpl idiom to use this? Right now I have the notion of a static Provider class which sits between the Engine and its Plugins.

quote:
3. Trying to make your system understandable and flexible is great goal to have. I would keep the creation of the Plugins out of the Engine itself, and wouldn't have any of the networking code be dependent/coupled with the higher level classes that contain it, i.e. Engine.

Yes, I'm finding out its damn hard to be simple as well. I have subcomponents of the Engine (the PluginManager, at the moment, creates a DLLManager, which loads all the DLL files in a directory and unloads them on destruction). Is that what you mean?

As always, thank you.

[edited by - antareus on February 15, 2003 1:07:27 AM]
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
antareus
antareus
To stimulate some more discussion I feel I should add a little more detail about the design in.

My top layer is a class called Engine.It contains a:
PluginManager - (was) responsible for loading plugins from DLLs. EventManager - dispatches events from plugins based on the attributes of the event (a notion of event direction)
Provider - static class that serves as a proxy class for plugins

The whole system works just by passing Events around. All plugins have a pure virtual Dispatch method. Within this method, clients downcast to the event type using dynamic_cast and then call their own event handler for it.
Note: yes, I''ve seen visitor pattern, but I don''t see how it could work here.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.