good practice tips needed: types & method arguments

  • Thread starter Thread starter Jeroen
  • Start date Start date
J

Jeroen

For a hobby project, I'm discovering the "undo/redo"- and "Command
objects"- design patterns. In my specific project, I want:

1. The command objects to just hold the data; and
2. The receiving/executing objects to perform the action.

Now please consider the following code:

/************ Receiving/executing object code snippet
****************/
interface ICommand { ... }
class NameChange : ICommand { ... }

internal class myObject :
{
public virtual void RedoCommand(ICommand command)
{
if (command is NameChange)
this.RedoCommand(command as NameChange);
else
command.Redo();
}
private void RedoCommand(NameChange command)
{
command.fNameBefore = this.fName;
this.fName = command.fNameAfter;
}
}
/*********************************************/

The problem I'm looking at is as follows. Whenever I create a new
command I need to add two things:

1. Another "if" block to the generic 'ExecuteCommand' for the
ICommand.
2. A specific 'RedoCommand' for the particular new command type.

Is there any way to set this up so that the 'generic' method doesn't
need the ugly/clobbering "if/else" structure?

Note that -although I've used some of it- I'm fairly new to the "<T>"/
generics feature of .NET 2.

Any help would be much appreciated.
 
Jeroen said:
The problem I'm looking at is as follows. Whenever I create a new
command I need to add two things:

1. Another "if" block to the generic 'ExecuteCommand' for the
ICommand.
2. A specific 'RedoCommand' for the particular new command type.

Is there any way to set this up so that the 'generic' method doesn't
need the ugly/clobbering "if/else" structure?

Yes, but it doesn't involve generics. The way is to abandon your initially
stated goal of having the command object only hold the data.

Instead, make a new class for each command that holds the needed context
data and references to whatever other objects are necessary. A common
design goes along these lines:

interface ICommand
{
ICommand Invoke(); // executes the command and returns a
corresponding Undo object
}

class SetNameCommand : ICommand
{
private MyDataObject _target;
private string _newName;

internal SetNameCommand(MyDataObject target, string newName)
{
_taget = target;
_newName = newName;
}

ICommand ICommand.Invoke()
{
string oldName = _target.Name;
_target.Name = _newName;
retuirn new SetNameCommand(_target,oldName);
}
}

It's not uncommon for these concrete command types to be nested, private
types declared inside the type on which they work (e.g. a document type).

class MyDocument
{
private class SetNameCommand : ICommand
{ ... }
}

It's also not uncommon for the command object to simply delegate back to the
target object, calling a private member function to actually do the work (in
the example above, it's a property setter, but it could be arbitrarily more
complex).

HTH

-cd
 
Is there any way to set this up so that the 'generic' method doesn't
Yes, but it doesn't involve generics. The way is to abandon your initially
stated goal of having the command object only hold the data.

Instead, make a new class for each command that holds the needed context
data and references to whatever other objects are necessary. A common
design goes along these lines:

interface ICommand
{
...
}

class SetNameCommand : ICommand
{
...
}

It's also not uncommon for the command object to simply delegate back to the
target object, calling a private member function to actually do the work (in
the example above, it's a property setter, but it could be arbitrarily more
complex).

HTH
-cd


Thanks a lot for the respons, it sheds some more light on this design
pattern.

I think though I may have followed your suggestion already. The
Command object from my first post in fact in my project is the actual
invoker, and delegates the (re)do/undo actions to the subject as you
proposed.

The solution for my own problem now also comes to me, thought I'd
share it here as well. I have a class called "CommandManager" that
combines the "Command" and "myObject" in the first place. It tells the
"Command" object which method to delegate (re)do/undo to:
"myObject.RedoCommand(ICommand x)" in the example in my first post. It
is at that point where I should already distinguish, and assign a more
specific method. In some fast-written (pseudo)code:

/*******************************************/
class CommandManager
{
// Method to activate and 'do' the command. Later the 'undo' and
'redo'
// of this commandmanager may re-invoke the command.
public void ActivateCommand(ICommand command, ICommandSubject
subject)
{
// ...

if (command is NameChange)
command.RedoHandler = subject.NameChange;

// ...
}
}
/*******************************************/

Again, thanks for the response.

-Jeroen
 
Back
Top