开发者

Need an idea on how to handle variable groups of tasks in a Winforms applicaton

I am struggling how to piece开发者_C百科s of decoupled actions in my application. I basically have a form that the user chooses actions to carry out. Some actions stand alone and easily could go in a Queue that I carried out in an order. However, some actions require multiple related actions that the main action requires other tasks to be handled first:

Create Website
 - DoesWebsiteExists (Validation)
 - CreatePhyiscalDir 
 - CreateWebsite 
 - CreateVirDirectories

All these actions have been separated and don't know about each other.

Originally, before I separated Validation from the action, I passing a Settings object that contains all related/needed settings to carry out any actions and a Queue that I iterate over and call the Execute() method on each action.

 Queue<IAction> Actions
    [0] = CreateDirectory.Execute()
    [1] = CreateWebsite.Execute()

But now that I have stripped all these action down, I am not sure how I could reassemble them.

Would I have a list of lists? Where If the action was an stand-alone action it would just be a list of 1 element? And if it was an action that required 4 actions (4 different objects) it would be a list of 4 elements?

To throw an additional layer in there - there is a chance that sometimes a main action would require an action or not depending on choices chosen on the form. For example if the user selected to create a Application Pool in IIS, that could be in the list or not, and coming up with a way to prioritize the list.

Any ideas?


Your IAction interface (not to be confused with the Action Delegate) looks a great deal like an implementation of the Command Pattern. In the past when I wanted to group commands together (.NET v1.0) I would have create a Composite pattern to create a composite command or in your case a Composite Action. So its implementation details would have an Add method and and an Execute looks something like..

public CompositeAction : IAction
{
    private ActiondCollection Action;

    void Add(Action action)
    {
       Action.Add(action);
    }

    void Execute()
    {
       foreach(Action action in Actions)
       { 
          action.Execute(); 
       }
    }
}

This in line with your list of lists idea.

You can do this but since Execute returns void the only way to stop a chain of commands from working is to throw an exception or some use some weird coupling (updating some IsValid value from DoesWebSiteExist and then inspect that value in every Action)

Instead you could implement the Chain of responsibility which will handle this more cleanly.

Another alternative, if you can modify the existing methods to just return a bool you can achieve the results you want without too much difficulty using Func

The nice thing here is as long as a method returns a bool it can participate in this structure. The bad thing is that its not enforced via an interface so the methods don't have as clear of a purpose so it has some code smell to it.

namespace Test
{
    class Program
    {
        //Change this value to show how the Actions bail when you DoesWebsiteNotExists 
        //returns false
        static bool isvalid = true;
        static void Main(string[] args)
        {
            List<System.Func<bool>> MainActions = new List<System.Func<bool>>();

            List<System.Func<bool>> CompositeActions = new List<System.Func<bool>>();

            MainActions.Add(() => NonCompositeAction());

           //Probably want a builder here.
            CompositeActions.Add(() => DoesWebsiteNotExists());
            CompositeActions.Add(() => CreatePhyiscalDir());
            CompositeActions.Add(() => CreateVirDirectories());
            CompositeActions.Add(() => CreateWebsite());

            MainActions.Add(() => ExcuteCompositeActions(CompositeActions));

            foreach (Func<bool> action in MainActions)
                action.Invoke();



        }


        static bool ExcuteCompositeActions(List<System.Func<bool>> Actions)
        {



            bool Success = true;
            foreach (Func<bool> action in Actions)
            {
                if (!action.Invoke())
                {
                    Success = false;
                    break;
                }

            }
            return Success;
        }

        static bool NonCompositeAction()
        {
            Console.WriteLine("NonCompositeAction");
            return true;
        }

        static bool DoesWebsiteNotExists()
        {
            Console.WriteLine("DoesWebsiteExists");
            return isvalid;
        }
         static bool CreatePhyiscalDir()
        {
            Console.WriteLine("CreatePhyiscalDir");
            return true;
        }
        static bool CreateWebsite()
        {
            Console.WriteLine("CreateWebsite");
            return true;
        }
        static bool CreateVirDirectories()
        {
            Console.WriteLine("CreateVirDirectories");
            return true;
        }






        }

    }

As for prioritizing I would do that up front rather than building up and then prioritizing.

Update To clarify the methods in the List<Func<bool> can be from anywhere. For example if you had this class

public class CreatePhyiscalDir
    {

        public bool Execute()
        {
            Console.WriteLine("CreatePhyiscalDir");
            return true;
        }
    }

You could change the above to

    CreatePhyiscalDir cpd = new CreatePhyiscalDir();
    CompositeActions.Add(() => DoesWebsiteExists());
    CompositeActions.Add(() => cpd.Execute());

This will work as long as you cpd is in scope.

I would like to stress that this strength of the List<Func<bool> is ...

  1. It allows you to create structures out of calls to methods that return a bool which can be almost anything. This is especially helpful in the case where you have a pre-existing code base.
  2. It creates a dependency that the next action won't be executed unless the previous one was successful.

But the weakness are...

  1. You may find yourself writing methods that return true; just to fit into the structure that otherwise would not be there.
  2. The relationships between the methods or classes are not at all clear.

So you have three options an actual implementation of Chain of Responsiblity or Pipeline, a pseudo Pipeline implementation like the List<Func<bool>> solution or a Composite Actionl like SnOrfus answer. Which you pick will probably depend on what you've written so far and how much of it you will have to change.

One final thought. You may want to first decide, does each Action decide if the next action should be taken or does the looping code decide. It may inform how you implement.


I like the solution posted by Conrad Frix, but I'll suggest another option as well:

I've done something similar, and in my case I used the chain of responsibility pattern, instead of having a collection of actions.

So, in your case I would change your IAction to be an abstract base class that looks like:

public abstract SystemAction
{
    public SystemAction nextAction { get; set; }

    public void Execute()
    {
        this.ExecuteAction();
        if (this.nextAction != null)
            this.nextAction.Execute();
    }

    protected abstract void ExecuteAction();

    public SystemAction ThenDo(SystemAction action) 
    { 
        this.nextAction = action;
        return this.nextAction;
    }            
}

So that your action could link to the next action to perform. Client code ends up looking like:

SystemAction setupSite = new CreatePhyiscalDir();
setupsSite
    .ThenDo(new CreateWebsite())
    .ThenDo(new CreateVirDirectories());

setupSite.Execute();

I wouldn't say this is any better than Conrad's solution, but is is another option. I put it to effective use in my system (building filter chains for a queuing mechanism).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜