My version of (replacement for?) MVC - thumbs up or thumbs down?
I would like to get opinions on whether the following design approach has potential. I use MVC and command pattern terminology here.
The model is a model. What more needs saying? It does not know anything about anything else. It just sits in its room listening to The Cure until someone comes around.
The view is a Swing GUI. It does not really know about anything else either (class names, APIs, etc.) except that it requires (in my particular application) a TableModel from the model to be passed to it when it is instantiated, and it allows CommandFactories to be registered on its various components.
The CommandFactories implement a common interface (CommandFactory) which stipulates that they be able to generate Commands (also an interface). Commands have execute, undo, and redo functionality. When the view gets input from the user it uses a CommandFactory to generate a Command and execute it, then stores a history of the generated and executed commands for undo and redo purposes.
A launcher class with a main method glues the view and model together by getting a TableModel from the model and instantiating the view using this and the various CommandFactories.
That is it, pretty much. The part that is confusing me most is how to register all the CommandFactories on the view. One option that had occured to me is to have the launcher pass the view a HashMap mapping familiar keys to command factories.. like:
// in the launcher
Model theModel =new Model();
TableModel theTableModel = theModel.getTableModel();
HashMap<CommandFactories> commandFactories =new HashMap<CommandFactories>();
commandFactories.add("add",new AddCommandFactory());
commandFactories.add("edit",new EditCommandFactory());
commandFactories.add("delete",new DeleteCommandFactory());
View viewy =new View(tableModel, commandFactories);
// in some part of the view
publicvoid actionPerformed(ActionEvent event){
CommandFactory theFactory = commandFactories.get(event.getActionCommand());
if (theFactory !=null){
// generate a command from the selected factory, execute it, store it in the history...
}
}
What do you think?
Drake
[2875 byte] By [
Drake_Duna] at [2007-10-2 13:25:09]

Sorry, I realized that my code must be mystifying on one point. Obviously the factories will have to know about the model so they can operate on it. So the lines where I instantiated the factories should have looked like this.
commandFactories.add("add", new AddCommandFactory(theModel));
commandFactories.add("edit", new EditCommandFactory(theModel));
commandFactories.add("delete", new DeleteCommandFactory(theModel));
Drake
Sounds interesting, though, I'd like to see more of the details filled in. A lot of things you say this will do are mentioned as if they are trivial but do not seem so to me.
What is the advantage of passing in a HashMap over:
viewy.register(new AddFactory());
viewy.register(new EditFactory());
viewy.register(new DeleteFactory());
Where View has a register method for each type of factory?
> Sounds interesting, though, I'd like to see more of
> the details filled in. A lot of things you say this
> will do are mentioned as if they are trivial but do
> not seem so to me.
I will bang out a very simple example today and post it up here.
> What is the advantage of passing in a HashMap over:
> > viewy.register(new AddFactory());
> viewy.register(new EditFactory());
> viewy.register(new DeleteFactory());
>
>
> Where View has a register method for each type of
> factory?
Well, if I pass the HashMap in then I get better technical separation. All the view knows is that it is getting CommandFactories or Commands or whatever to assign to its various controls. It does not know what kind of CommandFactories might exist. One of the ideas behind the design is to get a truly dumb view... one that knows nothing about the outside world except that there are Commands which can be assigned to its controls.
Drake
Nothing startling or new here.If you read the GoF book, you'll see that Command pattern is used so that a button and a menu in a UI can share the same executable class to accomplish a given function.I think they beat you to the punch by about ten years.%
> If you read the GoF book, you'll see that Command> pattern is used so that a button and a menu in a UI> can share the same executable class to accomplish a> given function.What is the GoF book?Drake
Also, what does using Action (presumably the class you were talking about) have to do with separation of model and view?Drake
http://www.amazon.com/gp/product/0201633612/sr=8-1/qid=1141123766/ref=pd_bbs_1/104-7886314-6901536?%5Fencoding=UTF8The original and still the best patterns book there is.%
> Also, what does using Action (presumably the class
> you were talking about) have to do with separation of
> model and view?
>
> Drake
No, I explicitly said Command pattern, and you'll have to read that chapter in the book to find out.
It has nothing at all to do with separating Model and View. It's really about Controller, isn't it? After all, that's who will instantiate and run the Commands. That pattern helps you modularize your Controller.
%
I will have to buy that bad boy when my new credit card arrives. I am trying to sort out a good setup here as I am about to start coding a new app.Drake
> Well, if I pass the HashMap in then I get better
> technical separation. All the view knows is that it
> is getting CommandFactories or Commands or whatever
> to assign to its various controls. It does not know
> what kind of CommandFactories might exist. One of
> the ideas behind the design is to get a truly dumb
> view... one that knows nothing about the outside
> world except that there are Commands which can be
> assigned to its controls.
So you have a single interface that supports adds, edits, and deletes? Can you post that interface? The other thing is that you are marking these in your map as 'edit' or 'add'. Why not create named methods so your have an explicit interface instead of a hidden/obscured one?
As Duffy rightly pointed out, this is known as the Command pattern. It is used when the maximum amount of decoupling between tiers is desired. If your application does not need that additional complexity, remember YAGNI and KISS.
The GoF book is basically the original design pattern 'bible'. It is indispensible. However, the examples are generally in C++. If you want a more J2EE oriented tome, pick up Fowler's "Patterns of Enterprise Application Architecture".
- Saish
Saisha at 2007-7-13 11:04:31 >

> As Duffy rightly pointed out, this is known as the
> Command pattern. It is used when the maximum amount
> of decoupling between tiers is desired. If your
> application does not need that additional complexity,
> remember YAGNI and KISS.
>
> The GoF book is basically the original design pattern
> 'bible'. It is indispensible. However, the examples
> are generally in C++. If you want a more J2EE
> oriented tome, pick up Fowler's "Patterns of
> Enterprise Application Architecture".
>
> - Saish
Fowler's PEAA is an excellent book, but it's far more comprehensive than GoF. I'd recommend a book that uses the GoF patterns but uses Java-based examples. I've heard that Mark Grand has such a book on the market:
http://www.amazon.com/gp/product/0471258415/sr=8-1/qid=1141145480/ref=sr_1_1/104-7910591-0562341?%5Fencoding=UTF8
There's the "Head First" book on patterns:
http://www.amazon.com/gp/product/0596007124/qid=1141145513/sr=2-1/ref=pd_bbs_b_2_1/104-7910591-0562341?s=books&v=glance&n=283155
I've heard that this book is excellent, too:
http://www.amazon.com/gp/product/0131489062/qid=1141145538/sr=1-1/ref=sr_1_1/104-7910591-0562341?s=books&v=glance&n=283155
But Saish is spot on: GoF is the best, especially if you can read the C++ and Smalltalk examples.
%
The Fowler PEAA book: http://www.amazon.com/gp/product/0321127420/qid=1141145612/sr=1-1/ref=sr_1_1/104-7910591-0562341?s=books&v=glance&n=283155%
Well, I went ahead and made an example. Somehow we got off on the wrong path here (at least, in terms of what I want). I am not looking for a medal or a place writing textbooks. I just want to write a good app. If I have reinvented a known pattern (maybe in an inferior or less developed form) that is fine.
I was able to achieve total ignorance of both the model and view. The Commands and launcher are the only "smart" objects. In this test application the model is just a StringBuilder. The view is a pretty boring Swing GUI, with a text field and four buttons.
One prepends the contents of the text field to the StringBuilder. One deletes the last three characters in the StringBuilder. The other two are undo and redo buttons. The contents of the model are always displayed on the title bar. That's it.
The only thing that stands out about the view is that it has a few public methods, most important of which is the command binding method:
public void bindCommands(Map<String, Command> commands) {
Command found;
// add, delete, undo, and redo are JButtons
found = commands.get("add");
if (found != null) add.addActionListener(new EventCommandBridge(found));
// EventCommandBridge just wraps a Command with an ActionListener
found = commands.get("delete");
if (found != null) delete.addActionListener(new EventCommandBridge(found));
found = commands.get("undo");
if (found != null) undo.addActionListener(new EventCommandBridge(found));
found = commands.get("redo");
if (found != null) redo.addActionListener(new EventCommandBridge(found));
}
What you see above is all the view knows about the outside world. As someone pointed out, you need info to come from the view, so since the execute() method of my Command interface does not take parameters, I had to add a couple of other public methods to the view.
public String getCurrentText() {
return textField.getText();
}
public void setTitle(String title) {
theFrame.setTitle(title);
theFrame.repaint();
}
The Commands use these methods to get their business done.
You can probably guess the rest. The PrependTextCommand and DeleteThreeCommand spawn new objects when executed that remember what they do to the model, and are registered with an UndoRedoManager. Since the UndoCommand and RedoCommand are meta-commands, obviously, they are not registered on the manager and thus need not spawn any objects - they just work with the manager's public methods directly.
The whole thing is glued together by a launcher class, the entire text of which is:
public class Launcher {
public static void main(String[] arguments) {
StringBuilder model = new StringBuilder();
UndoRedoManager manager = new UndoRedoManager();
Viewy view = new Viewy();
HashMap<String, Command> commands = new HashMap<String, Command>();
commands.put("add", new PrependTextCommand(model, view, manager));
commands.put("delete", new DeleteThreeCommand(model, view, manager));
commands.put("undo", new UndoCommand(manager));
commands.put("redo", new RedoCommand(manager));
view.bindCommands(commands);
view.display();
}
}
There are kinks to be ironed out. For one thing, I know I have some common functionality in PrependTextCommand and DeleteThreeCommand that can be factored out. But basically I am pretty pleased with the pattern.
If you see any problems with this, please let me know, as I am thinking of using it soon. I am also happy to hear about specific ways that have already been devised to improve on this.
Unfortunately it seems that I cannot send the entirety of the code to my FTP folder from work, but I will put it there when I get home and come back to post the address here.
Thanks.
Drake
Here is that link: http://www.immortalcoil.org/drake/patterntest.zipDrake
> public void bindCommands(Map<String, Command>
> commands) {
>
> Command found;
>
> // add, delete, undo, and redo are JButtons
>
> found = commands.get("add");
> if (found != null) add.addActionListener(new
> w EventCommandBridge(found));
>
> // EventCommandBridge just wraps a Command
> Command with an ActionListener
>
> found = commands.get("delete");
> if (found != null) delete.addActionListener(new
> w EventCommandBridge(found));
>
> found = commands.get("undo");
> if (found != null) undo.addActionListener(new
> w EventCommandBridge(found));
>
> found = commands.get("redo");
> if (found != null) redo.addActionListener(new
> w EventCommandBridge(found));
> }
I still don't understand why you think the above is better than just supplying a set of methods like:
bindAddCommand
bindDeleteCommand
...
The only thing that using a map does is obscure your interface. Anytime you have a long list of similar ifs, you should consider it a bad code smell.
> Well, I went ahead and made an example. Somehow we
> got off on the wrong path here (at least, in terms of
> what I want). I am not looking for a medal or a
> place writing textbooks. I just want to write a good
> app. If I have reinvented a known pattern (maybe in
> an inferior or less developed form) that is fine.
>
Command pattern. A good one to implement in complex systems that need maximum decoupling.
> I was able to achieve total ignorance of both the
> model and view. The Commands and launcher are the
> only "smart" objects. In this test application the
> model is just a StringBuilder. The view is a pretty
> boring Swing GUI, with a text field and four
> buttons.
>
I'd be interested to actually see this occur. Controllers and views are normally tightly coupled. Really, the Command pattern, if it resides anywhere in MVC, is in the controller. As such, the controller needs to know which model functionality to invoke. In your case, it is simply a StringBuilder. However, one could easily take a more complex example of say, Account and Debit. These are model classes that the command must have dependencies with. The model is dependent on nothing. Most other tiers depend one way or another on the model.
> One prepends the contents of the text field to the
> StringBuilder. One deletes the last three characters
> in the StringBuilder. The other two are undo and
> redo buttons. The contents of the model are always
> displayed on the title bar. That's it.
>
I like how you took a simple problem and tried to see how the MVC and Command patterns could fully be implemented. However, I hope you would admit that the solution, as implemented, is over-designed.
> The only thing that stands out about the view is that
> it has a few public methods, most important of which
> is the command binding method:
> > public void bindCommands(Map<String, Command>
> commands) {
>
> Command found;
>
> // add, delete, undo, and redo are JButtons
>
> found = commands.get("add");
> if (found != null) add.addActionListener(new
> w EventCommandBridge(found));
>
> // EventCommandBridge just wraps a Command
> Command with an ActionListener
>
> found = commands.get("delete");
> if (found != null) delete.addActionListener(new
> w EventCommandBridge(found));
>
> found = commands.get("undo");
> if (found != null) undo.addActionListener(new
> w EventCommandBridge(found));
>
> found = commands.get("redo");
> if (found != null) redo.addActionListener(new
> w EventCommandBridge(found));
> }
>
As Dubwai pointed out, a series of if statements usually means you can refactor the checks in question into an interface or abstract class. Add a method such as likes() which returns a boolean based on the object passed in.
> What you see above is all the view knows about the
> outside world. As someone pointed out, you need info
> to come from the view, so since the execute() method
> of my Command interface does not take parameters, I
> had to add a couple of other public methods to the
> view.
Nope. It only seems that way. The view is dependent on the model. No way around that. Now, in your case, the 'model' is just ole reliable java.lang.String. But for a more complex application, it is simply not possible to totally disaggregate the view from the model. The view exists to display model data. The dependency cannot be removed. (You can add levels of indirection by adding additional tiers or converting strongly-typed model data to something loosely-typed like a Map, but the dependency still exists).
> > public String getCurrentText() {
>
> return textField.getText();
> }
>
> public void setTitle(String title) {
>
> theFrame.setTitle(title);
> theFrame.repaint();
> }
>
> The Commands use these methods to get their business
> done.
>
Is that what gets the business done? Or is it simply responding to a user's action and displaying the results of your model? The model is where the meat of your system is. Again, it is hard to see in your example because the model is a simple String.
> You can probably guess the rest. The
> PrependTextCommand and DeleteThreeCommand spawn new
> objects when executed that remember what they do to
> the model, and are registered with an
> UndoRedoManager. Since the UndoCommand and
> RedoCommand are meta-commands, obviously, they are
> not registered on the manager and thus need not spawn
> any objects - they just work with the manager's
> public methods directly.
>
I'm not sure I understand your distinction between vanilla and meta-commands.
> The whole thing is glued together by a launcher
> class, the entire text of which is:
> > public class Launcher {
>
> public static void main(String[] arguments) {
>
> StringBuilder model = new StringBuilder();
> UndoRedoManager manager = new UndoRedoManager();
> Viewy view = new Viewy();
>
> HashMap<String, Command> commands = new
> ew HashMap<String, Command>();
>
Right, your Frame or your main() method are normally your application's top-level controllers, organizing additional controllers and views.
> commands.put("add", new PrependTextCommand(model,
> l, view, manager));
> commands.put("delete", new
> ew DeleteThreeCommand(model, view, manager));
> commands.put("undo", new UndoCommand(manager));
> commands.put("redo", new RedoCommand(manager));
>
> view.bindCommands(commands);
> view.display();
> }
> }
>
Why place the Command objects in a Map and then pass the whole Map to the view? Wouldn't it be easier to simply add a method to the view (or an abstract view superclass) such as 'addCommand' or 'bindCommand', as I believe Dubwai also suggested.
> There are kinks to be ironed out. For one thing, I
> know I have some common functionality in
> PrependTextCommand and DeleteThreeCommand that can be
> factored out. But basically I am pretty pleased with
> the pattern.
>
Refactoring is good.
> If you see any problems with this, please let me
> know, as I am thinking of using it soon. I am also
> happy to hear about specific ways that have already
> been devised to improve on this.
>
> Unfortunately it seems that I cannot send the
> entirety of the code to my FTP folder from work, but
> I will put it there when I get home and come back to
> post the address here.
>
> Thanks.
>
> Drake
- Saish
Saisha at 2007-7-20 21:54:42 >

I find the best way to build an abstraction layer between the view and themodel is to use the Builder Pattern. http://en.wikipedia.org/wiki/Builder_pattern
> The only thing that using a map does is obscure your> interface. Anytime you have a long list of similar> ifs, you should consider it a bad code smell.I agree with both of these comments.%
Thanks for the comments, everyone. I have two points to follow up on:
1. The Map idea
You guys are right, this is stupid. The reason I got off down that path in the first place was that I was thinking I could pass the Map in as a parameter to the view's constructor, and then I would not need any public methods for binding the commands. But I needed a reference to the view to instantiate the Commands, so I had to instantiate it first, with the result that I could not handle this in the constructor for the view. So it ended up creating a public method anyway, rendering the whole approach pointless.
I think the reason I didn't agree earlier is that I misunderstood what was being suggested. I thought I was being told to have the view instantiate the Commands. I do not like this idea, because the whole point is for the view to be ignorant of anything outside itself. Which brings me to point #2...
2. What I mean when I say the view and model are ignorant
I claimed that I had achieved this, and within the constraint of what I mean when I say that they model and view are ignorant, I think I was right.
> Nope. It only seems that way. The view is
> dependent on the model. No way around that. Now, in
> your case, the 'model' is just ole reliable
> java.lang.String. But for a more complex
> application, it is simply not possible to totally
> disaggregate the view from the model. The view
> exists to display model data. The dependency cannot
> be removed. (You can add levels of indirection by
> adding additional tiers or converting strongly-typed
> model data to something loosely-typed like a Map, but
> the dependency still exists).
Of course the view is "dependant" on the model. It needs to display stuff and it has to get that stuff from the model one way or another. But in the exact same since the model is "dependant" on the view.
What I was after was total ignorance of both the view and model of the API of any other component - i.e. the view knows jack about the controller or model and the model knows jack about the view or controller. For the model to be ignorant in this regard is normal, but in most of the applications I see the view knows about the model's API, or it knows details about the controller's API. In my setup, it only knows that there is such a thing as a Command with an execute() method that it should invoke.
I am starting to get a pretty clear picture that all I have actually done here is successfully use the Command pattern with Swing. Maybe the gap of understanding here is caused by a difference in resources. To you guys this stuff is all really obvious, but my only resource right now is the Internet.
I have found good pages addressing the Command pattern outside the context of GUI/model separation. But all the sites I have found addressing the Command pattern in combination with Swing and MVC type issues have had designs that made me go "Uuuh... that is not the Command pattern as I understand it".
One site seemd to think that putting business logic into classes that extended AbstractAction was implementing the Command pattern, and in a book I found online the guy was subclassing JButton and having the same class implement Command, then using ActionEvent.getSource() to get the button, cast it to a Command, and call its execute() method.
If you know of any good online materials on this I would appreciate links, so I can make sure there aren't any other details I am screwing up.
As a final note on the difference between types of Commands, here is the execute() method of one of the Commands that spawns effects registered on the UndoRedoManager:
public void execute() {
PrependTextCommand.CommandEffect effect = new PrependTextCommand.CommandEffect(textProvider.getCurrentText());
effect.doIt();
manager.register(effect);
}
And here is the execute method of the UndoCommand:
public void execute() {
manager.undo();
}
I.e., the undo command is not registered on the manager, or else it could itself be undone and would block the stack so that you could not do deep undos. That is all I was trying to say about that.
Drake
> Thanks for the comments, everyone. I have two points
> to follow up on:
>
Drake, let me just preface the aforementioned and the following by saying your presence on the fora is a welcome addition!
> 1. The Map idea
>
> You guys are right, this is stupid. The reason I got
> off down that path in the first place was that I was
> thinking I could pass the Map in as a parameter to
> the view's constructor, and then I would not need any
> public methods for binding the commands. But I
> needed a reference to the view to instantiate the
> Commands, so I had to instantiate it first, with the
> result that I could not handle this in the
> constructor for the view. So it ended up creating a
> public method anyway, rendering the whole approach
> pointless.
>
Yes. In that implementation. However, it is bad for another idea. Maps are loosely-typed. Really, most people deal with Map<String, String>. Generics make it possible for a Map to have more type-safety, but IMO, you should always think first of a POJO (plain ole Java object) in a Collection, which might be a Map. How you access that POJO is dependent on your requirements. But the Collection chosen is an implementation decision, ideally.
> I think the reason I didn't agree earlier is that I
> misunderstood what was being suggested. I thought I
> was being told to have the view instantiate the
> Commands. I do not like this idea, because the whole
> point is for the view to be ignorant of anything
> outside itself. Which brings me to point #2...
>
You're right. It has a bad smell. The very name 'controller' should connote responsibility for integrating model and view.
> 2. What I mean when I say the view and model are
> ignorant
>
Hard for the view to be. Essential for the model to be.
> I claimed that I had achieved this, and within the
> constraint of what I mean when I say that they model
> and view are ignorant, I think I was right.
>
Somewhat. The view is now dependent on the Command object. You have added a level of indirection. Really, this points out the difficulty in disaggregating controller from view. The controller really is designed to service a certain type of view (Swing, Servlet, etc.). So, from the get-go, it is designed for a certain view. In reality, it is hard to design a view without also thinking about the controller. A view that had no knowledge, at all, of a controller would not properly be able to allow a user to interact.
> > Nope. It only seems that way. The view is
> > dependent on the model. No way around that. Now,
> in
> > your case, the 'model' is just ole reliable
> > java.lang.String. But for a more complex
> > application, it is simply not possible to totally
> > disaggregate the view from the model. The view
> > exists to display model data. The dependency
> cannot
> > be removed. (You can add levels of indirection by
> > adding additional tiers or converting
> strongly-typed
> > model data to something loosely-typed like a Map,
> but
> > the dependency still exists).
>
> Of course the view is "dependant" on the model. It
> needs to display stuff and it has to get that stuff
> from the model one way or another. But in the exact
> same since the model is "dependant" on the view.
>
No. Never repeat that. Sorry, this is a point near and dear to my heart. The model is what makes your system unique. It is hard to see with java.lang.String. However, views and controllers normally are very similar in what they do. The model is where you earn your money.
> What I was after was total ignorance of both the view
> and model of the API of any other component - i.e.
> the view knows jack about the controller or model and
> the model knows jack about the view or controller.
> For the model to be ignorant in this regard is
> s normal, but in most of the applications I see the
> view knows about the model's API, or it knows details
> about the controller's API. In my setup, it only
> knows that there is such a thing as a Command with an
> execute() method that it should invoke.
>
The view must know about the controller. It initiates actions the controller is expected to respond to. The controller somewhat is dependent on the view. It must know what actions to take invoking model functionality. This is why most MVC implementations are in fact MD (model-delegate, with delegate being the mish-mosh of coupled view and controller). Separating the two is hard work. Complex work. However, it is only a fixed cost. If the benefit in reducing overall system complexity outweights this fixed cost, then it should be undertaken. (Knowing when this occurs is far more art than science, sadly).
> I am starting to get a pretty clear picture that all
> I have actually done here is successfully use the
> Command pattern with Swing. Maybe the gap of
> understanding here is caused by a difference in
> resources. To you guys this stuff is all really
> obvious, but my only resource right now is the
> Internet.
>
Don't lose heart. The Internet has a lot of great resources. Maybe I am traditional, but I do read the traditional paper books, and only after reading reviews of the author on the Internet. There are a few stellar authors you cannot go wrong with: Alur (traditional, overly-engineered but essential if you want to understand where design was and where it is going), Fowler (my favorite author, bar none), Johnson (an excellent skeptic of the orthodoxy originally espoused by Alur, et al), Raible (somehow seeming unbiased through all the trends) and many others. The original Group of Four patterns book is the bible. Understanding other OO languages like SmallTalk and C++ provide excellent contrasts. And seeing the offerings of aspect oriented programming is invigorating, if intimidating. Contract oriented languages such as Eiffel help understand where Java's shortcomings might be improved by documentation and design. The list goes on. (In all honesty, no one here can keep up with everything).
> I have found good pages addressing the Command
> pattern outside the context of GUI/model separation.
> But all the sites I have found addressing the
> e Command pattern in combination with Swing and MVC
> type issues have had designs that made me go "Uuuh...
> that is not the Command pattern as I understand it".
>
It's a simple idea. Think of a strongly-typed version of HTTP. You have dedicated POJO request and response objects. Each tier interacts with them. They define a contract. In simple request-response transactions, the Command may be a single object (or implementation of an interface, better yet). For more complex interactions, dedicated Request and Response objects may have to be written.
>
> One site seemd to think that putting business logic
> into classes that extended AbstractAction was
> implementing the Command pattern, and in a book I
> found online the guy was subclassing JButton and
> having the same class implement Command, then using
> ActionEvent.getSource() to get the button, cast it to
> a Command, and call its execute() method.
>
Please, I am trying to have a good night's sleep.
> If you know of any good online materials on this I
> would appreciate links, so I can make sure there
> aren't any other details I am screwing up.
>
See above.
> As a final note on the difference between types of
> Commands, here is the execute() method of one of the
> Commands that spawns effects registered on the
> UndoRedoManager:
> > public void execute() {
>
> PrependTextCommand.CommandEffect effect = new
> w
> PrependTextCommand.CommandEffect(textProvider.getCurre
> ntText());
> effect.doIt();
> manager.register(effect);
> }
>
> And here is the execute method of the UndoCommand:
> > public void execute() {
>
> manager.undo();
> }
>
> I.e., the undo command is not registered on the
> manager, or else it could itself be undone and would
> block the stack so that you could not do deep undos.
> That is all I was trying to say about that.
>
> Drake
Fair enough. :^)
- Saish
Saisha at 2007-7-20 21:54:42 >

