If you want to create a Batch-like functionality, use the Command Pattern.
Below is a very simple implementation of the pattern. The idea is to have a unified interface for all classes you want to invoke. Each class encapsulates one operation in the batch:
interface BatchCommand
{
public function execute();
public function undo();
}
One class implementing the interface will be the commander of all subclasses:
class BatchCommander implements BatchCommand
{
protected $commands;
public function add(BatchCommand $command)
{
$this->commands[] = $command;
}
public function execute()
{
foreach($this->commands as $command) {
$command->execute();
}
}
public function undo()
{
foreach($this->commands as $command) {
$command->undo();
}
}
}
A simple Command could look like this:
class FileRename implements BatchCommand
{
protected $src;
protected $dest;
public function __construct($src, $dest)
{
$this->$src;
$this->dest;
}
public function execute()
{
rename($this->src, $this->dest);
}
public function undo()
{
rename($this->dest, $this->src);
}
}
You could then use it like this:
$commander = new BatchCommander;
$commander->add(new FileRename('foo.txt', 'bar.txt'));
$commander->add(/* other commands */);
$commander->execute();
Because BatchCommander is a BatchCommand itself, you can easily stack batches belonging together into other Batches, thus creating a very flexible tree structure, e.g.
$batch1 = new BatchCommander;
$batch1->add(/* some command */);
$batch1->add(/* some other command */);
$batch2 = new BatchCommander;
$batch2->add(/* some command */);
$batch2->add(/* some other command */);
$main = new BatchCommander;
$main->add($batch1);
$main->add($batch2);
$main->execute();
In your Check/Test context, this means you could group conceptually belonging single tests into a test suite. And you can create test suites of test suites, by stacking one suite into another.
Of course, you could also give the BatchCommander a file path to check on instantiation and have it init all BatchCommands by running through the files in the file path. Or pass it a Factory instance to use for creating the Check commands.
You don't have to have execute
and undo
for method names. Name it check
if you want. Leave out undo
if you don't need it. The basic idea still stays the same though: one interface for all classes to be commanded, whatever that may look like. Feel free to adapt.
An alternative with a somewhat different UseCase is the Chain of Reponsibility pattern. Check it out to see if this would be of utility too.