John Hofman's Design Study
Contents |
My Project
This design study was introduced by my ENEL428 software assignment. The purpose of the assignment was to design a prototype of an Instant Messenger System using concurrent programming. The system was broken into two separate parts a client and a server. This design study is regarding the client program.
Design Study
The initial system uses a simple login model, a user attempts to log in with just a username which the server accepts or rejects.
Functional requirements of the client:
- Connect to a server.
- Login.
- Logout.
- Display the other users online.
- Start a conversation with another online user.
- Post Messages in a conversation.
- Invite other online users to a conversation.
- Leave a conversation.
Constraints:
- C++
- Uses POSIX threads for concurrency.
Room for Expansion:
- Other protocols, XMPP etc.
- Other GUI implementations, currently uses fltk-1.1.9
Initial Design
This is the design which is fully functional.
Description
The initial design is event driven. There are two threads that respond to events, the Inbox and the GUI.
- The Inbox responds to messages from the server. It receives, decodes and executes packets that it gets from the socket. It executes the packets by calling operations on the UserManager and Chat objects.
- The GUI responds to input from the user. It calls operations on the UserManager and Chat objects according to the input from the user.
The UserManager and Chats use Mutexs to synchronise access to their data. They also use the Outbox in some operations to write packets to the socket. The relationships between the classes are designed to remove any cyclic dependancies from the chain of operations triggered by an event from the GUI or server:
Inbox->Socket
Inbox/GUI->UserManager/Chat->Outbox->Socket
Class Descriptions
- Socket/ClientSocket: The Socket encapsulates a TCP Socket. It provides an interface to read/write packets to the socket. The ClientSocket has a different constructor because it uses the IP address of the server to connect. There are problems with this part of the design and will be discussed below.
- Inbox: Inherits from a thread class. Reads packets from the ClientSocket, unpacks them and executes operations on the Chats and the UserManager. The Inbox also contains a map that links Chat objects to their ChatID's (This is nasty, why does the inbox have this map).
- Chat: Each chat represents a conversation with another user. The chat maintains a list of messages but not the participants of the conversation (YAGNI vs OCP?). Chats are subjects in the Observer pattern so they notify their observers whenever their state changes.
- UserManager: The UserManager keeps a record of the other users online. The server pushes updates to the client when users log on and off so they can be added and removed from the UserManager. The user manager also handles the login/logout actions of the local user. The UserManager is a subject in the Observer pattern so it notifies its observers if its state changes.
- Outbox: The outbox builds packets to write to the socket. It is used by Chats and the UserManager to send information to the server. The outbox currently records the userID of the local user so it can tag outgoing packets, this seems like it shouldn't be there. (If it wasn't there then the outbox is just behavior without state, except a reference to the Socket)
- Mutex: Wraps a POSIX threads mutex and condition variable. The interface can lock and unlock the mutex. I also implemented an interface to wait and signal the condition variable, but that is unused in the current design.
GUI:
- Factory/Fl_Factory: Sort of implements the factory pattern, in a strange way. It makes Chats and UserManagers and also makes the associated Fl_Window'ed objects (Fl_WindowedChat and FL_WindowedUserManager) which are then linked with the observer pattern.
- Fl_WindowedChat: Encapsulates the GUI for a Chat. In an observer of a Chat. Implemented using fltk-1.1.9
- Fl_WindowedUserManager: Encapsulates the GUI for a UserManager. Is an observer of a UserManager. Implemented using fltk-1.1.9
Stuff that needs fixing, maybe?
- Chat map in Inbox. Fix: Add chat manager but get (law of demeter vs repeat code) e.g. chat_manager.getChat(id).Receive(message) vs ChatManager having the same interface as chat (except also with a ChatID function parameter) so it can pass the call to the correct chat.
- Switch smell in Inbox to deal with packets? Polymorphism..? Which means that Socket will need a switch.
- Chat doesn't record participants, might need that for other protocol (YAGNI vs OCP)
- Socket/ClientSocket inheritance is weird, socket should just be an interface so that it is extendable.
- Packet should know how to serialise itself.
- Outbox shouldn't record who is logged in.
- Names of classes are a bit meh.
- State switch in Fl_WindowedUserManager, switch statement smell -> State pattern...
Good Stuff:
- observer decouples GUI from implementation, coupled with Factory means you can build whatever kind of GUI you fancy.