9

I've implemented the command pattern (in a multi-support way) in my application.

Structure:

class MultiCommand : BaseCommand

abstract class BaseCommand : ICommand

Process Flow:

   var commandsGroup = new MultiCommand(new List<ICommand>()
            {
                new Command1(),
                new Command2(),
                new Command3(),
            });

   commandsGroup.Execute()

Now, suppose that in Command1 a somethingID is changed and I'll use this new value in Command2... And also, that there are plenty of other properties and objects that are being affected during the whole execution process.

Also, there are some interface implementations that should be available at any command just using the context object like:

Context.ServerController.something();

The instantiation of the IServerController will take place just before the multiCommandGroup initialization.

How can I have a shared context like this for all Commands of the group?

Example of the Context class:

public class CommandContext
{
    public IServerController ServerController;
    public RequiredData Data { get; set; }

    public CommandContext(){}
}

IMPORTANT A minimal implementation Code is here

3
  • why don't you make MultiCommand generic? Commented Jun 17, 2016 at 14:27
  • See stackoverflow.com/questions/104918/… Commented Jun 17, 2016 at 14:34
  • I've checked them all @Greg but in this case I'll have many objects as inputs and how can I get the updated object from the other commands? Commented Jun 17, 2016 at 14:38

7 Answers 7

5
+150

1) If you want to keep this interface, then you have to pass this context as constructor parameter:

new MultiCommand(new List<ICommand>()
            {
                new Command1(context),
                new Command2(context),
                new Command3(context),
            })

2) As another option you can accept list of delegates instead of list of commands. MultiCommand will be look like this:

class MultiCommand : ICommand
{
    public MultiCommand(List<Func<Context, Command>> commands, Context context)

}

That is almost the same except MultiCommand is responsible for all the commands share the same context.

3) Looks like commands in MultiCommand depends on result of previous command. In this case Command pattern is not probably the best. Maybe you should try to implement Middleware chain here?

interface IMiddleware<TContext>
{
   void Run(TContext context);
}

class Chain<TContext>
{
    private List<IMiddleware<TContext>> handlers;

    void Register(IMiddleware<TContext> m);

    public void Run(TContext context)
    {
        handlers.ForEach(h => h.Run(context));
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

which one do you prefer?And what about cons and pros of all?Performance?
I provided some more info.Please check
Well, it dependes on context's lifetime. If it is a long-living object, then it's better to have stateful commands with context injected through contstructor.
so, something that I did? Can you provide a more detailed sample?
Oh, I see your code example. I see one problem here: you have to manually care for Commands to share single context. In this case Middleware is more suitable. Please see example gist.github.com/vkorolev/afb72425062946d6f6d92562f7da6b4b
|
3

I would suggest to make somethings generic. Here is a super simple example.

class MultiCommand<TContext>  
{
    List<Command<TContext>> Commands;
    TContext Context;
}

6 Comments

seems nice I'll try it and I'll come back.
@DanielA.White what about thread-safety?Do I have to care about this because the whole execution process is running in a new thread,not the main?
@GiannisGrivas that is a separate question in of itself. theres way too many possibilities.
Ok @DanielA.White indeed this is something else.thanks
@DanielA.White it would be great if you could help here.stackoverflow.com/questions/37889973/… thanks.
|
3

You could have a constructor on your BaseCommand class (and its derived classes) that would accept a Context class of some kind. When instantiating the commands that will belong to the same group, you could provide them all the same context object. Maybe something like:

public class CommandContext
{
    // The object that will be the target of the commands' actions.
    public object Data { get; set; }

    // ... any other properties that might be useful as shared state between commands...
}

public abstract class BaseCommand : ICommand
{
    protected CommandContext Context { get; private set; }

    public BaseCommand(CommandContext ctx)
    {
        Context = ctx;
    }
}

public class ChangeSomethingIDCommand : BaseCommand
{
    public ChangeSomethingIDCommand(CommandContext ctx) : base(ctx)
    { }

    public void Execute()
    {
        var target = (SomeDomainClass)Context.Data;
        target.SomethingID++;
    }
}

// Elsewhere in your code (assuming 'myTargetDomainClassInstance' is
// a SomeDomainClass instance that has been instantiated elsewhere and
// represents the object upon which the commands will do work):
var ctx = new CommandContext { Data = myTargetDomainClassInstance };
var commandGroup = new MultiItemCommand(ctx, new List<ICommand>
    {
        new ChangeSomethingIDCommand(ctx),
        new Command2(ctx),
        new Command3(ctx)
    });

commandGroup.Execute();

3 Comments

@Greg, only in the sense that the constructor of its base class, BaseCommand, requires it. I doubt MultiItemCommand itself has much use for it. If the BaseCommand constructor doesn't throw an exception for a null ctx parameter, then the MultiItemCommand class could expose a constructor that doesn't accept a CommandContext instance, and just pass null to the base constructor.
@wablab based on your approach I updated my implementation.Is this right?thanks
Without knowing the requirements of your app, it's tough to say how well this approach will work for you. It does appear that your implementation is along the lines of what I was describing.
3

Consider a Functional Style

public class SomeMainClass{
   public void MultiCommandInit()
    {
        MultiCommand.New()
            .Add(new Command1())
            .Add(new Command2())
            .Add(new Command3())
            .SharedContext(CC => {

                CC.Data = new RequiredData();
                CC.ServerController = GetServerController();
            });

    }

    private IServerController GetServerController()
    {
        // return proper instance of server controller
        throw new NotImplementedException();
    }
}

Requires this extension method / function...

  public static class XMultiCommand
    {
        //  How can I have a shared context like this for all Commands of the group?
        public static MultiCommand SharedContext(this MultiCommand mc, Action<CommandContext> CallBack)
        {
            var cc = new CommandContext();            
            CallBack(cc);
            mc.SharedContext = cc;
            return mc;
        }

    }

Finally, these changes to MultiCommand

public class MultiCommand
{
    private System.Collections.Generic.List<ICommand> list;
    public List<ICommand> Commands { get { return list; } }
    public CommandContext SharedContext { get; set; }

    public MultiCommand() { }
    public MultiCommand(System.Collections.Generic.List<ICommand> list)
    {
        this.list = list;
    }
    public MultiCommand Add(ICommand cc)
    {
        list.Add(cc);
        return this;
    }

    internal void Execute()
    {
        throw new NotImplementedException();
    }
    public static MultiCommand New()
    {
        return new MultiCommand();
    }
}

Cool Things Happen Using Functional Styles

  1. Re-usability soars!
  2. Hyper focus on Single Responsibility concerns
  3. Composition becomes the Norm
  4. Code Maintenance becomes simple
  5. Intellisense becomes your built-in API (just use code commenting)
  6. No radical OOP design patterns are needed
  7. Fluent code becomes very enjoyable to work with
  8. Nested / Decorated Functions are much more easy to imagine and implement
  9. You will never repeat youerself
  10. The Open/Closed principal becomes your religion
  11. Code is now always Clear, Complete and Concise
  12. Some even say no interfaces are needed any longer

1 Comment

I am fan of Functional too! Thanks for the approach.It could be my final choice for implementation!
3

In your case, going with injecting context through constructor is fine as mentioned by others. But in general, I would go with injecting the context through method parameters instead:

public class Command1: BaseCommand
{
    //inject as parameter instead
    public void Execute(Context ctx)
    {

    }
}

The reasons are:

  • The context should be managed by CommandGroup so that we have better encapsulation.
  • The CommandGroup is responsible for executing its list of commands so that it's possible for the CommandGroup to pass to each Command only the parameters each Command really needs, these parameters may be constructed at runtime (maybe by previous Commands) so that it's not possible to pass in these objects as the time we construct the list of commands. Therefore, it's easier to reuse Command and also simplify unit testing these Commands as we don't need to construct the whole context object in unit tests.

Maybe you don't need to care about these things at the moment, but method injection gives more flexibility. If you have worked with some frameworks in .NET, you would see something similar like OwinContext, FilterContext,.. they are passed as parameters and contain relevant information for that context.

In my opinion, your case is not a good fit for Command pattern. A Command represents a user request (action) and these objects could be created dynamically at runtime, but you're predefining your Commands at coding time.

What you're trying to do looks like owin middleware or asp.net web api message handler which are http://www.dofactory.com/net/chain-of-responsibility-design-pattern

Comments

2

And what about changing your approach? I did an architecture for DDD recently and executing a commad implies atomic operation (retrieve aggregate root from persitence, apply domain rules and pesist the aggregate) so I do not in needed of a share context and can batch multiple commands whithout worries.

Here you have an cqrs architecture that use command pattern with the above strategy I posted.

1 Comment

check the description and it would be great if you could adjust your approach to my implementation.thanks
2

My 0.02:

1) The MultiCommand class looks like a Composite pattern.

You may want to add a GetParentCommand() method at the base command class and add an AddChildCommand() method at the MultiCommand class, which set every children's parent.

Then the children commands could get the context object from its parent. (Context object should also be defined in base class. And it may be of generic type.)

edit:

abstract class BaseCommand<T> : ICommand
{
    public T Context { get; set; }
    public BaseCommand Parent { get; set; }
}

class MultiCommand : BaseCommand 
{
     public void AddChildCommand(BaseCommand command) 
     {
         command.parent = this; // we can get parent's context from children now
         // put the command in an internal list
     }
}

var commandsGroup = new MultiCommand();
commandsGroup.AddChildCommand(new Command1());
commandsGroup.AddChildCommand(new Command2());
commandsGroup.AddChildCommand(new Command3());

commandsGroup.Execute()

2) We may create a global singleton context object. In MultiCommand's Execute function, we could set the current context object before executing children's Execute function. Then child command could just access the singleton context object. And after all children's execution, the MultiCommand could reset the context. (The context is actually a stack here.)

edit:

abstract class BaseCommand : ICommand
{
     // it could be put anywhere else as long as it can be accessed in command's Execute
     // it can also be a stack
     public static CommandContext Context {get; set;} 
}

class MutliCommand : BaseCommand 
{
    public void Execute()
    {
        // do something to BaseCommand.Context

        ChildCommand.Execute();

        // do something to BaseCommand.Context
    }
}

class ChildComand: BaseCommand 
{
     void Execute() 
     {
          // do something with BaseCommand.Context
     }
}

Another option is to put the context object as a parameter of the Execute function:

class MultiCommand : BaseCommand 
{
     void Execute(CommandContext context) 
     {
         Children.Execute(context);
     }
}

2 Comments

can you provide a code sample.It seems interesting.
"context object as a parameter of the Execute function:" it is a solution .thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.