• 0

[C#] Command pattern


Question

I'm creating a reporting application that will create lots of different kinds of reports, so I'm implementing the Command pattern to encapsulate the function of actually creating the report.

I have the following classes:

- Report. The Command. Abstract base class, contains abstract Start() method.

- MyReport1Report. Inherits Report, implements Start() method to create the physical MyReport1 file.

- MyReport2Report. Inherits Report, implements Start() method to create the physical MyReport2 file.

- ReportHandler. The Invoker. Contains a Queue of reports to execute, and an Add(Report rpt) method to allow you to add reports to the queue to be executed.

The Main class gets the next concrete Report object to execute from a ReportList class, adds it to the ReportHandler, and calls execute via the ReportHandler. Internally the ReportHandler class also contains a method to handle what to do when a Report completes.

So I've completely, and probably incorrectly, by-passed the Receiver class from the Command pattern. Could someone point out why a Receiver class is needed in this example?

Link to comment
Share on other sites

8 answers to this question

Recommended Posts

  • 0

Well, no, the ReportHandler class isn't really doing anything that the receiver class normally would, unless from the command class you make calls back to the ReportHandler class. The Receiver is intended to be the action that the command performs. Here is how I've used the command pattern in the past:

public abstract class Command
{
  #region Properties
  public Receiver Receiver { get; private set; }
  #endregion

  #region Constructors
  public Command(Receiver receiver)
  {
	this.Receiver = receiver;
  }
  #endregion

  #region Methods
  public abstract bool Execute();
  public abstract bool Rollback();
  #endregion
}

public abstract class Receiver
{
  #region Methods
  public virtual void Action()
  {
	return true;
  }

  public virtual void Rollback()
  {
	return true;
  }
  #endregion
}

public class Invoker
{
  #region Fields
  private List<Command> commands = new List<Command>();
  private Stack<Command> stack = null;
  #endregion

  #region Methods
  public void AddCommand(Command command)
  {
	commands.Add(command);
  }

  public void ExecuteAll()
  {
	if (stack != null)
	  throw new InvalidOperationException("A subsequent call after a previously incomplete ExecuteAll() requires a RollbackAll() call.");

	stack = new Stack<Command>();
	foreach (Command command in commands)
	{
	  if (command.Execute()) 
	  {
		stack.Push(command);
	  } 
	  else 
	  {
		RollbackAll();
		// Todo: throw an exception here after RollbackAll().
	  }
	}
  }

  public void RollbackAll()
  {
	if (stack == null)
	  throw new InvalidOperationException("A call to Rollback was not required.");

	for (int i = 0; i < stack.Count; i++)
	{
	  Command command = stack.Pop():
	  if (!command.Rollback())
	  {
		// Todo: RollbackAll() has failed because a command failed to Rollback.  Throw an exception here.
	  }
	}

	stack = null;
  }
  #endregion
}

public class MyCommand : Command
{
  #region Constructors
  public MyCommand(Receiver receiver) : base(receiver) { }
  #endregion

  #region Methods
  public void Execute()
  {
	this.Receiver.Action();
	return true;
  }

  public void Rollback()
  {
	this.Receiver.Rollback();
	return true;
  }
  #endregion
}

public class MyReceiver : Receiver
{
  #region Methods
  public override bool Action()
  {
	Console.WriteLine("Custom action called.  This is normal execution.");
	return true;
  }
  public override bool Rollback()
  {
	Console.WriteLine("Rollback action was called.  This usual indicates a failed command or command-chain.");
	return true;
  }
  #endregion
}

public class MyTestClass()
{
  static void Main()
  {
	Invoker invoker = new Invoker();
	Receiver receiver = new MyReceiver();

	Command command = MyCommand(reciever);

	invoker.AddCommand(command);

	invoker.ExecuteAll():

  }
}

Your report handler is taking on the task on my Invoker.

The receiver class is not always required, sometimes the Command's Execute (or Start) method accomodates all the required functionality of the Receiver class.

Link to comment
Share on other sites

  • 0

The Command design pattern contains a Receiver, Invoker and Command class. When I tried to implement the Command pattern I've managed to do it without using a separate Receiver class. The ReportHandler class is both the Invoker and Receiver... but I don't think that is correct... ?

Link to comment
Share on other sites

  • 0

I don't think you really have a problem here. If your app doesn't need to separate the actions from the objects they act on then there will inevitably be some simplification of the pattern, and one class will fill more than one role. I really wouldn't worry.

Link to comment
Share on other sites

  • 0
...

The receiver class is not always required, sometimes the Command's Execute (or Start) method accomodates all the required functionality of the Receiver class.

Thx for the example. Where did you put the code logic for what the command physically does - in the command or receiver? I thought it would go in each individual command class... but then what is the receiver for?

In my case, each command class (Report) will create a different kind of file. So the code for creating that file goes in each command class's execute method.

I don't think you really have a problem here. If your app doesn't need to separate the actions from the objects they act on then there will inevitably be some simplification of the pattern, and one class will fill more than one role. I really wouldn't worry.

Yeah I think I'm going to end up with a semi-command pattern :)

But its got me thinking about the purpose of the Receiver...

Link to comment
Share on other sites

  • 0
Where did you put the code logic for what the command physically does - in the command or receiver? I thought it would go in each individual command class... but then what is the receiver for?
The receiver class is not always required, sometimes the Command's Execute (or Start) method accomodates all the required functionality of the Receiver class
Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.