3

I have a Chain of Command pattern implemented, with modules that implement an interface:

interface IRequestHandler
{
    public function handle(&$offset, &$tripData, $request = null);
    public function setSuccessor(OffsetRequestHandler $handler);
}

I set up a chain of modules that modify the $offset and $tripData objects if necessary (I guess that it is not exactly a chain of command pattern, but it gets the job done :)

I would like to start using dependency injection whenever it is possible to ease the process of writing unit tests. My question is: how to handle it, if each module may need different set of services? I can go with Auryn, and let it automatically instantiate all dependencies for each module (at least I hope that I can), but that shouldn't be necessary in theory...

Update:

To build the chain I take names of classes from the config file, I instantiate them, and I arrange them in a queue. The controller is not aware of the dependencies that each module needs. Right now, there are none, because each module creates needed objects by itself (and this is what I want to change). I don't want to inject each module with the container, because this will turn into a Service locator pattern.

Update 2:

The need:

I get a set of data from external resources, and I need to process it to get some metrics. However, the data may be corrupted, sent in batches, or whatever, and my script needs to fix it (as much as possible). The types of problems may depend on the external resource the data comes from. And the situation is dynamic, meaning that new types of problems, and new external resources may appear in the future.

My idea:

Create modules. Each module will address specific problem. Arrange the modules in a queue (as the order of the modules may be important), and execute them injecting the incoming data as a parameter.

The solution is heavily inspired by this article on SitePoint.

But instead of creating the instances one by one like in the example:

$firstHandler = new FirstHandler();
$secondHandler = new SecondHandler();
$thirdHandler = new ThirdHandler();

//the code below sets all successors through the first handler
$firstHandler->setSuccessor($secondHandler);
$secondHandler->setSuccessor($thirdHandler);

$result = $firstHandler->handle($request);

I pull the names of the classes from the config file, and I create them with a foreach loop:

$modulesQueue = $config->get("extensions");

foreach ( $modulesQueue as $moduleName ) {
    $moduleName = "extensions\\".$moduleName;
    $startModule->setSuccessor(new $moduleName());
}
// Starting the chain:
$ret = $startModule->handle($offset, $tripDataObject, $requestData);

The problem:

If I create instance of the classes like that, I don't see what each module needs to have injected (at this moment nothing, as there is no DI applied - each module instantiate services it needs). So I see two options here: either I switch to manual creation of the modules (which is not flexible at all), or I start using Reflection to find out what each module needs, and provide it.

  • 1
    What do you mean by "how to handle it?" – Greg Burghardt May 9 '18 at 13:20
  • Good question - I have updated my description @Greg – george007 May 9 '18 at 14:36
  • Can you give a short example (in code) of how you currently try to inject the dependencies, and where you run into issues? – Doc Brown May 9 '18 at 16:06
  • ... currently, I think it is pretty unclear why you don't just inject a MockRequestHandler into your command objects in your unit tests. Just don't use the config file mechanism in your tests, what is the problem? – Doc Brown May 9 '18 at 18:42
  • 1
    Done @DocBrown. Thank you for your interest in my problem :) – george007 May 11 '18 at 11:21
1

Ok, so the core problem is to create create command handler objects generically, but constructor injection enforces individual parameters for each command handler.

This can be easily solved by separating the construction process for the chain from the actual constructor call utilizing a corresponding factory class for each of your handler classes. Each factory constructor stays parameterless, but inside each factory the handler objects will be constructed with the required services injected.

By using using a strict naming scheme like FirstHandlerFactory for FirstHandler, SecondHandlerFactory for SecondHandler etc, the resulting code will then look like

foreach ( $modulesQueue as $moduleName ) {
    $factoryName = "extensions\\".$moduleName."Factory";
    $startModule->setSuccessor((new $factoryName())->buildModule());
}

(Note I did not fix that this code does not set up a chain of command, to keep things simple, but I am sure you get the idea.)

The buildModule function then may look like this

 class FirstHandlerFactory
 { 
     function buildModule()
     {
        return new FirstHandler(/* provide the individual services here */);
     }
 }

In your unit tests, you will be able to create your handler objects just with mock services injected, as required for the specific test, without using the factories.

This solution does neither require a DI framework, nor some reflection mechanism.

  • Thank you @Doc Brown. So it seems that it is either "one class to rule them all", but this class would need something like a Reflection to identify what each module needs, or a Factory class for each module, where I can manually define injected services. I haven't thought about this approach. It seems that it may the best one (although it hurts me when I think that it needs twice as much classes, and it can't be easily simplified ;) ). – george007 May 14 '18 at 7:44
  • @george007: using separate factory classes gives another advantage: the command handler classes can be grouped into a module on its own, where the module is completely independent from the services. The services reside in a different module, independent from the command handlers, and the factories in a 3rd module which depends on the others and binds them together. So in a well modularized system, "one class to rule them all" makes no sense, having separate classes for this is a standard approach and should not cause any superstitious fears about getting "too many classes". – Doc Brown May 14 '18 at 8:40
3

Do you know what Pure Dependency Injection is? Dependency Injection doesn't require use of Dependency Injection Containers. You mention Auryn, which is a container.

Nothing is inherently wrong with using a container but like all frameworks their authors are eager to let you make yourself dependent on them. Allowing that to happen to your code base has the consequence of effectively changing the language your codebase is written in. You can't advertise the job as a PHP job. It's a PHP/Auryn job. Now you have to find PHP/Auryn programmers.

Rather then let the container redefine your language you can contain it. Don't allow anything in your code base but your composition root (hopefully this is just main) even know that Auryn exists and most of your code base can be maintained by normal PHP programmers.

To build the chain I take names of classes from the config file, I instantiate them, and I arrange them in a queue. The controller is not aware of the dependencies that each module needs. Right now, there are none, because each module creates needed objects by itself (and this is what I want to change). I don't want to inject each module with the container, because this will turn into a Service locator pattern.

This sounds more like a testing/refactoring problem. Unit Testing is most expensive and least effective when introduced late. So don't expect this to be easy. It sounds like you have something that works. Do you have integration tests? It's much easier to refactor to unit testable code if you have some kind of tests in place. It's just slow because integration tests are slow.

If you're new to Auryn it's likely a good idea to practice by writing some code from scratch that works with it. Make it in the ideal form you'd like your codebase to be. See how that works.

Now you have a model of what you're going for as you refactor your existing code base and have some experience using Auryn. Next just break off small parts of your code base. Write unit tests for them and bring their construction under Auryn's responsibility. If Auryn makes this difficult at first try a pure DI solution. Either way stop the classes from building their own dependencies or you're doing this for no reason. Instead build them outside and pass them in. Bit by bit you'll transform your code base into something unit testable.

Keep in mind, the point of unit tests isn't to "test all the classes!". It's to help me read code. You likely have some boring glue code that has no real behavior code in it. Do not kill yourself trying to test that. I can read that code and predict it's behavior without help. But if any interesting behavior code is mixed in there move it out into something testable. Doing this is called the humble object pattern. It's the same as when people insist on moving logic out of views and into presenters.

If "interesting" seems subjective understand I'm asking you to stop thinking about the structure of your code and think about how it looks to a newbie. Give them a unit test that shows how something is used, what the scope of code that must be read to understand it, make clear what you expect it to do, and you'll have newbies that can easily find that one line of code they need to edit. Use your tests to help me read code. Please.

  • Auryn tries to stay away from being called a container, and is sometimes called a library for automated injection (or something like that). I have mentioned it, because as far as I understand it uses Reflection to understand what the class needs, instead of being informed about it when bootstrapped (like in Pimple). Anyway, while I agree with you that in general using a library is not necessary, but that does not solve my problem :) – george007 May 9 '18 at 14:44
  • @george007 better? – candied_orange May 9 '18 at 15:20
  • One way to make your code container-independent is to create an IContainer interface and then write a shim for your chosen container that conforms to the interface. That way, if you ever need to replace the container, you can simply write a new shim. – Robert Harvey May 9 '18 at 20:20
  • Awesome @CandiedOrange, thank you. It is great that you found time to explain it like that. I would only argue that the purpose of unit tests is also to automate the deployment process, while the code can be easier to read when split into more functions that are private, and their names tell what their purpose is. Secondly, I brought up Auryn here, because it seems to be most useful in this scenario. It is because Auryn doesn't need to be pre-configured, and will create necessary services when my modules will be created. This comes at the cost: these services won't be singletons. – george007 May 10 '18 at 11:34
  • Perhaps I should start with another question: does the way my code is arranged now (the Chain of Command-ish pattern) is the best solution for my needs. Then I should see how I can do DI and unit tests. I think it is best to post this question in another topic, what do you think? – george007 May 10 '18 at 11:36

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.