Frage

I have a class that follows the Command Pattern. It has 2 methods which are Execute, and CanExecute which checks whether to invoke Execute or not (they derive from ICommand). CanExecute invokes a few methods that check that all required services are running, the version is correct, etc.

After CanExecute is invoked, it may fail and return false and I need to know why. Is it because of a bad version, services, missing file, etc.

What is the best strategy to know what is the problem

One option is whenever a required condition fails I can throw an exception that will describe the error in the message field. However the possibility that it will fail is expected and you shouldn't use exceptions for regular flow of control. So I'm really not sure.

Thank you.

War es hilfreich?

Lösung

You can use a collection of "reasons" that will tell the users of the class why CanExecute returned false. The reasons can be a simple IEnumerable<string>.

public bool CanExecute() {
  var messages = new List<string>();

  if (!Condition1) {
    messages.Add("Missing Condition1");
  }

  ...

  Messages = messages;
  return messages.Count == 0;
}

public IEnumerable<string> Messages { get; private set; }

Then, client code can show the collection of messages to end-users.

UPDATE:

You can also associate new commands with the messages to give the users ways to fix the problems found. In this case, instead of an IEnumerable<string>, you can create your own class that encapsulates that information:

public class Message {
  public string Text { get; set; }
  public ICommand Command { get; set; }
}

...

public bool CanExecute() {
  var messages = new List<Message>();

  if (!Condition1) {
    messages.Add(
      new Message { 
        Text = "Missing Condition1", 
        Command = new FixCondition1Command() 
      }
    );
  }

  ...

  Messages = messages;
  return messages.Count == 0;
}

public IEnumerable<Message> Messages { get; private set; }

Andere Tipps

UPDATE: Reworked based on feedback.

Since the UI needs the reasons CanExecute() returns false, two things come to mind:

Option 1: Add an enumerable message property to the command interface and populate it as needed during the call to CanExecute(). The UI could then interrogate the property as needed. If you go this route, make sure you clear out the contents of the property each call to CanExecute() so you don't lose track of state.

public interface ICommand
{
    IEnumerable<string> Messages { get; }
    bool CanExecute();
    void Execute();
}

public class SomeCommand : ICommand
{
    public IEnumerable<string> Messages { get; private set; }

    public bool CanExecute()
    {
        var messages = new List<string>();

        var canExecute = true;

        if (SomeCondition)
        {
            canExecute = false;
            messages.Add("Some reason");
        }

        if (AnotherCondition)
        {
            canExecute = false;
            messages.Add("Another reason");
        }

        Messages = messages;

        return canExecute;
    }

    public void Execute() { }
}

Option 2: Have CanExecute() return an object which contains the bool as well as an enumerable messages property. This makes it obvious that the messages only apply to that call of CanExecute(). However, depending on where/how you're implementing (e.g. data binding), this could complicate other scenarios more than you're looking for.

public class CanExecuteResult
{
    public bool CanExecute { get; set; }
    public IEnumerable<string> Messages { get; set; }
}

public interface ICommand
{
    CanExecuteResult CanExecute();
    void Execute();
}

public class SomeCommand : ICommand
{
    public CanExecuteResult CanExecute()
    {
        var result = new CanExecuteResult { CanExecute = true };
        var messages = new List<string>();

        if (SomeCondition)
        {
            result.CanExecute = false;
            messages.Add("Some reason");
        }

        if (AnotherCondition)
        {
            result.CanExecute = false;
            messages.Add("Another reason");
        }

        result.Messages = messages;

        return result;
    }

    public void Execute() { }
}

Obviously, the specifics of how you want to handle the interfaces, enumerable types, etc. is up to you. The code is just a representation of the idea.

    Bool CanExecute()
    {
     if(!CheckXXX)
      throw new Exception("CheckXXX function throws an exception")

     if(!CheckYYY)
      throw new Exception("CheckYYY function throws an exception")

    if(!CheckZZZ)
     throw new Exception("CheckZZZ function throws an exception")

    return true; //everything is working fine    
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top