This is a multi-part message in MIME format. ------=_NextPart_000_00E2_01C2A510.266E7C70 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Hi Sergio, > We should firstly show the onlooker one of the aspects that > was bothering me when I said I thought it was too convoluted. > That is, your switch can handle either an ON condition or an > OFF condition. It cannot handle both. To do so you would need > to add another intermediate messaging class LightOffCommand > (if we follow your previous naming convention). Your lamp > would need to know whether it is currently on and ignore > requests to turn on or whether it is currently off and > ignore requests to turn off. A real switch cannot be > turned on if it is already on, or off if it is already off. > So to model the switch accurately you would have to build the logic into > the switch not the lamp (as I have in fact done). Oh, sorry, I was purposely just keeping the example as simple as I could to get the idea of using the pattern across. My fault, I'm probably not explaining this too well :) Yes the switch would know its state and only turn "on" when it is "off". As your code did. It would hold two Commands, a "LightOnCommand" and a "LightOffCommand". > If we have a motor with three input levels on it I would want > it to be a different motor to one that only has an off/on input. > I would only have a problem with the motor class if I tried to > keep all my options open by adding on, off, left, mid, right, > up, down, sideways, insideout blah blah blah to a motor base > class. By using pointers to methods you can point to a > method in one class that does not exist in another class and > visa versa. I agree about not polluting the base class. Again, I don't think I explained that very well. I was trying to show a way that you could deal with a more "analogue" value. One that would be parameterised. The example was probably not that strong. I was thinking along the lines of a servo motor system - the motor would have a range of positions. It makes no sense to have a function for each of these positions on your motor class, rather to have a single method to set a position. Lets say for arguments sake, that this is a simple CNC mill. The position of the servo is important throughout the entire program and would be controlled by the "SetPosition()" method when the machine is active. This is a somewhat contrived example, I would actually model this differently in a real system, but the purpose here is to demonstrate the Command Pattern, not a "complete" system. Now, we want to attach an operator interface so they can manually move things around and move the tool onto and off of the work piece. So, we want to implement the following commands with switches (or buttons where only the press is important in this case, just to make the example a little simpler). - "Home" - move the X and Y servos to (0,0). Perhaps this is so the tool can be changed by the operator - "Center" - move the X and Y servos to (100, 100). - "Step Left" (X axis) - "Step Right" (X axis) - "Step Forward" (Y axis) - "Step Back" (Y axis) From my perspective, it would be inelegant to implement a function for each of these on the ServoMotor. And perhaps the "Home" position is arbitrary and changes lots - the ServoMotor shouldn't care about where that really is, it should be just managing the motor and going where it is told. Otherwise you are introducing unnecessary dependency on other parts of the system into the ServoMotor class (so it can find where home is). Refer to the attached C++ file. Note I've put most of the functions in the class definitions, but I would normally not do this. It is just for brevity. This neatly (in my opinion) separates the "button" which is part of the user interface from the servo motors which are part of the main program so there is absolutely no coupling between them. This means that we are free to change either without needing to look at the other. In both the methods for handling the commands, code needs to be written that knows what to do to the target. the Command Pattern places this code in reusable classes which are bound only to the public interface of the servo motor. Therefore the implementation of the servo motor is free to change without affecting anything else. Note also that the are two "generic" commands implemented, the "SetPositionCommand" and the "StepCommand". These two are used for each of the 6 different commands. Not shown in this example is the CNC Milling Command interpreter. The cool thing (I think) about the Command Pattern in this case is the interpreter can be written to use exactly the same Commands (SetPositionCommand, StepCommand) as the interface. Or more to the point, the Interpreter would define a series of useful Commands that would be reused in the interface. Then the code to manage the movement is located in only one place - the concrete implementation of each command-derived class. Then at any stage, you can add new commands to the interpreter or the interface and the other system can utilise them easily. Also, see how easy it is to produce a macro that will execute and arbitrary sequence of commands. This could be useful for interface buttons and in the CNC command interpreter as it will allow the construction of complex commands from previously implemented simple commands, again saving rewriting code. The MacroCommand was implemented using the standard template library's list template just for convenience, it could be handled any way.. But why reinvent the wheel? :) > Provided you apply the method to the right type of object there is no > conflict, and you certainly don't have to worry about > propagating virtual > methods all over the place. > > sw1->attach(m1, &MOTOR_ONE_SPEED::on); > > sw2->attach(m2, &MOTOR_TWO_SPEED::on); > sw3->attach(m2, &MOTOR_TWO_SPEED::high); This is one of my problems with the pointer mechanism - you are able to accidentally specify the wrong object and the compiler will happily take your word for it and crash. You loose the nice type safety that is provided by the C++ compiler and this is not a thing to take lightly. Also, there is nothing wrong with virtual method calls, sure there is marginally more overhead, but we're talking about a user interface here, you will never notice it. They are central to the way objects work in C++.. > I think the problem here is that you are locked into the > virtual method mode of thinking. In which you have a pointer > to a base class and you want to apply a method to an object > of a derived class. You see the only way to > access the method is by having a virtual instance in the > base. Naturally all overloaded methods must take the same > parameters. BUT if you scrub the virtual method and point > directly to the real method, then the name becomes > just a label AND two methods with the same name in two > different classes CAN have different parameters and be > accessed via a pointer to method without conflict. And this is where I think we are going to disagree. I accept that your method works and is a valid solution BUT if I was writing an application I would rather avoid it. I think that you are locked into the "C" way of doing "call-backs" and are not taking full advantage of the language features in C++. Let us agree to disagree :) However I feel the entire point of C++ is the virtual method / inheritance / overriding / etc paradigm. When I write software, I want to use C++ BECAUSE of these things - I don't see them as a hindrance that needs to be worked around. I want my code to be type safe to prevent mistakes like calling the function on the wrong object. I want to have the simplest interfaces on objects, and ensure that each object is a simple as it can be so they are easy to test and verify. I want the various modules of code as well decoupled as possible so that I can take code and reuse it in both the same program and in others. Now, please don't take this as me saying: "This is the right way, and the only way". I'm not saying that at all, I'm just illustrating an alternative system which I think has some benefits over the pointers to member functions method. One other point - the Command pattern can be implemented in OO languages that don't support pointers, like Java and Smalltalk etc. Not that I would be writing in them :) > > Should we move this to [OT]? > > > I don't know that there is much more I can really say about this. Fair enough, I thank you for the discussion - I enjoyed it. Cheers, Ash. --- Ashley Roll Digital Nemesis Pty Ltd www.digitalnemesis.com Mobile: +61 (0)417 705 718 -- http://www.piclist.com hint: The PICList is archived three different ways. See http://www.piclist.com/#archives for details. ------=_NextPart_000_00E2_01C2A510.266E7C70 Content-Type: application/octet-stream; name="example.zip" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="example.zip" UEsDBBQAAAAIACxwkC07huEFjAQAAAwPAAAIAAAAbWFpbi5jcHCtV0tv2zgQPsuA/8OkBQrHadzk sBe7CdBkn0DaDTa7i+gU0BJlE5VEgaLsZoP0t+/wIYqUlKTo7iUWZ8hvHt9wOHnNyiRvUgrva5ky vtieTyevnSxntTyPonfvoJakTIlIQdKiyomkkLO1IOIe1J7pZDrBTX9uKZB1LQVJJKxJTSHJSV1D xgWQPIdLXhQIU08nRm7X08nDdFI165wly+kk2jEhG5LDV6ueHa486Y6zFH76QpNG0tkhnMEJah9X ygO7f7nsTj48Wtd+bwTcULHjH7nkwnnQiaDnRadBKw/FXXbNa2VtcZKtjDmdFiqh4jWTjJcL+G1T ckFr4I0EnoEg5YbCjuQNrRcqBOX6DZXX9sAMspwTCdknulfoh+hD9KD+RM6e1akMdFY3nlWUGJRf POBDBaRwBJWNKMHAdSCVYDvkcOkOuw1GbxPGSklFRhIKVVNv142UvFSRmNxd6HU/b3j0EndJ0SSY u7cgyWfMCIGk5Toy52Yt+zCvLosUnMvFnV6fgfoJ4/6Rtri4/mpx3EHcIGhOVdlxdN4aBJbBnsKW 7FBcUrWRZTP49NfVFRycgbWmQKIoxeNY2UYWmr7E+qUp7Le0hAo5rmnaMnptljND37MGzOL43NXv KCUuMdaRyKPllVc/r9oULrpydsoWZAmGG5fuZ/mCuqIJy5iiDC+sxAtdEUEKTIsw91iJOi6HBmf+ nZpXt/rjbSiMrdCW/+3fJHeLGBdBMRgEVQ/ma2XlsZPHvlyjqYujfltZbGWxlVlex3tKnw7fdefP aqjo3HB3ykRmHXC3q98HXN9yLmiO+mVkE3EY5OX4PGgobfwmiD5CHCLELyG0zfNG0qoj3ZYayp6q sf+3xDpDvdoKq0jtu2H/0KB4XI0EJeK2nnXH/mtZDMn3kEPmu4A8ymGUc0sY+OEMCGvFwQsAR74P HZ0tfx9JIvg3EujvVa+hCqZ7pkPtSAo/pOmg26++Oc84lyyXash432LM4Rxjy2vZzhPuZvmu4BjQ 89tkGKsxVe8Iv9eFVmPqsLEnbjSJ9luW0xkcBCYWOPbIewTB8BUZLpwEzkJnFpngpTStPQo1Fa/u PK3m49HVRej8WNJcACRNtfMl3bt3TnItoviJs4f6NJNZ3wd8ye/WJPk8A8fEUz70+pEeA7ETyK3g zWbr39MaZwXgIqU46KED1BxUO4qnKFwuGd54ouqb6XuDkxawQTbXdMOwnFeoOujrqC44YEdHo7TM NW6U9N9bGzBON1AQVrbheRf69sMXVtv2ErvvVTsM/MoLasefaTvPaGE72ihexp7GNz7wm9hbqLnS /MVQtJvG1CVVM9jAmBF/v7nTE2PQ/FqTxqJu9lc0kwOjSqMU1qDflwNLx6ejmH+wzXYcVGteQh0H /ZmLvfq3ZAzW6saA45fdvcA7Mgp7oS/P85gDSH2xOrig+84LVa0KcNBKowKrV7eC7+DYMOyVVAj2 ZKZ/CKvQhq59aysOPe5ik9hO9VVfaEl3ERZuNFZQfs2GmrawhlJdGUOxZXaoUNwE0jbXI2ZRZf89 OjU98F9QSwECFAAUAAAACAAscJAtO4bhBYwEAAAMDwAACAAAAAAAAAABACAAtoEAAAAAbWFpbi5j cHBQSwUGAAAAAAEAAQA2AAAAsgQAAAAA ------=_NextPart_000_00E2_01C2A510.266E7C70--