7

I have a 'sequence of tasks' to undertake on a zip file. It can be simplified into these steps:

  1. Validate that the file is a ZIP file and is valid
  2. Unzip the file
  3. Validate the contents are as expected
  4. Turn the contents into a product

I am currently using the Command pattern and it looks a little something like this

interface Command {
    public function execute($data);
}

class ValidateFile implements Command { 
    public function execute($filePath) { //... }
}

class UnzipFile implements Command { 
    public function execute($filePath) { //... }
}

class ValidateContents implements Command {
    public function execute($unzippedFilePath) { //... }
}

class ProductGenerator implements Command {
    public function execute($unzippedFilePath) { //... }
}

Now, each of these execute commands returns a string that is in turn (by the command invoker) passed to the next command in the chain (defined by configuration).

I am having two problems here:

  1. Each command requires its own dependencies, i.e. unzip needs access to ZipArchive, a class that handles the unzipping of zip files. How do I inject each command's depenencies? I am quite happy to get around this by instantiating all of the commands through a DIC and plugging them into an 'Invoker' that accepts instantiated objects of type Command, the runs them in sequence, however it doesn't feel right.

  2. (More of an issue) How do I persist more than a string between commands? I have seen in places people passing a 'context' object from one command to another, however this feels a bit dirty as the command would be depending upon an untyped interface (they essentially wrap an associative array). It makes sense that it has to be untyped, as if you're to maintain an interface between commands, then the context can't implement any specific methods as that would deter from its abstraction.

I would appreciate any advice on how to deal with both of these issues.

Edit: Another issue I have is that each of these commands is actually coupled to the state of the file path that's passed to it, they're quite specific to the case at hand, but I doubt it's possible to work past that.

  • "however it doesn't feel right." - what exactly does not "feel right"? It is quite normal that each command contructor may have a different signature with different dependencies. Your DIC approach seems reasonable, so what is bothering you? – Doc Brown Sep 5 '14 at 11:21
  • I don't really know, I wasn't 'feeling' it at the time, aha. You're right though, it is normal - I think I just need affirmation :P. – Matthew Haworth Sep 5 '14 at 11:24
5

To your first question: it is quite normal that each command constructor may have a different signature with different dependencies, so constructing the commands cannot be done fully generic. Using a DIC can indeed mitigate this.

For more complex scenarios, you could also use the "abstract factory" pattern, where you have one factory class per command, all of them derivated from an abstract factory interface, and each factory encapsulating the specific tasks for creating the individual command. These factories then can be passed to a generic "FactoryInvoker". But you should think twice if this is really worth the additional overhead in code.

To your second question: passing a 'context' object from one command to another is fine. Generic solutions sometimes come for the price of reducing some type safety. If you think you really need that much safety, make "context" an abstract base class, with different subclasses each one representing a different type of context. But beware, this can easily lead to a totally overengineered and unflexible solution, which may cause a lot of effort when you have to change the flow of commands afterwards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.