Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included here. The IssueI have a class that handles messages, based in part on the "Chain of Responsibility" design pattern. A very watered down possible implementation for my message handler class would look something like this: class IMessageHandler { private: IMessageHandler* m_pmhParent; protected: virtual bool OnMessage(int iMessageID,void* pvMessageParms){return(false);} public: CMessageHandler(IMessageHandler* pmhParent):m_pmhParent(pmhParent){} ~CMessageHandler(){SetParent(0);} void SetParent(IMessageHandler* pmhParent){m_pmhParent=pmhParent;} IMessageHandler* GetParent(){return(m_pmhParent);} bool HasParent(){return(GetParent()!=0);} bool HandleMessage(int iMessageID,void* pvMessageParms) { if(OnMessage(iMessageID,pvMessageParms)) return(true); else if(HasParent()) return(GetParent()->HandleMessage(iMessageID,pvMessageParms)); else return(false); } }; As you can see, IMessageHandler has a single member, called m_pmhParent. This stores a pointer to the message handler's parent. If m_pmhParent is 0(i.e. a null pointer), then the message handler is said to have no parent, and would be at the root of the tree. There is, of course, no limitation to the number of message handlers that can call another message handler its parent. In fact, at least in this implementation, there is no limitation restricting a message handler from being its own parent (but this could be simply fixed in the SetParent function, so for now ignore it). SetParent and GetParent are obviously a way of managing the assignments of parents to message handlers. The HasParent function is a shorthand to keep us from having to write if(GetParent()!=0) all the time. The constructor sets up a message handler with an initial value for its parent, and the destructor eliminates that value, resetting the parent pointer to 0 (not that it really matters in this implementation). The interesting functions are OnMessage and HandleMessage. Since OnMessage is virtual (and protected so that it cannot be called directly), we can assume that it is meant to be overridden in derived classes. This implementation simply returns false, as IMessageHandler knows nothing about how to handle any sort of message whatsoever. In another implementation, this function should perhaps be made pure virtual, as IMessageHandler itself should not be instantiated as it does nothing useful. HandleMessage is where the "glue" between message handlers lies. It first tries to handle the message with its own OnMessage function. Failing that, it checks for a parent. If a parent exists, it passes the message on down to the parent, who repeats the process until the message is handled or the top of the chain is reached and the message is still unhandled, in which case false is returned. Thus far, this solution is the best that I have found for basing my applications on. My application object is a subclass of IMessageHandler, as are various types of event handlers (this concept is often represented by a window), and I have also used it for finite state machines and its component states, custom user interfaces, and so on. Typically I go with a richer set of operations, like child management, but on some platforms, IMessageHandler has looked almost exactly like the implementation I have here. So, what exactly is the problem? My main issue is the parameter list for HandleMessage and OnMessage. Both take an int (which identifies the type of message being sent), and a void pointer, which is the most generic way I could come up with to send a variable amount of data that I could cope with. I could have gone the "…" route with these functions, but I try my best to stay away from "…", as it is even more difficult to manage than a void pointer parameter, at least in my opinion. Even with the int and void*, there are huge problems that crop up: Problem #1: the int parameter, iMessageID, is the parameter used to differentiate types of messages. How, then, shall we ensure that our applications do not duplicate message ids, since these must be unique? Often, when defining derived classes of IMessageHandler, we will be assigning values to message ids that will wind up hidden in the implementation. If, for example, we create a custom UI and create a CButton class to represent a command button, we will naturally want a message id reserved for clicking a button. As a developer, I don't really care what the values are for a particular message id, and I really shouldn't have to care. I simply want to use some sort of identifier to send and check for messages. Problem #2: a void* parameter is a poor way to store a variable number of parameters, especially in a strongly typed language like C++. It approaches being as bad as using "…". Many types of messages will have no parameters at all, which makes the parameter a waste. A quit message sent to an application needs no additional data. If we were simply concerned about overrunning the data buffer, we could add a size_t parameter that specifies how large the data being sent is, but that just compounds the useless parameter issue.
|
|