As there is no right or wrong in this question, I could give an example about how I like to use Dependency Injection in PHP.
I'm using the Dependency Injection Component from Symfony with the LosoDiAnnotationsBundle and extracted everything from the Bundle to use it as standalone as the Dependency Injection Component. I'm not using the rest of the Symfony Framework.
There are two functions in my system which use the Dependency Injection Container. The main function and the dispatcher. The main function (application entry point) retrieves the configured Dispatcher like this:
$dispatcher = $service_container->get('Dispatcher');
$response = $dispatcher->route( $request );
The service called "Dispatcher" is either defined in the global dependency config or annotated in the class itself like this:
/*
* @Service
*/
class Dispatcher {
/**
* @Inject({"@service_container"})
*/
public function __construct($serviceContainer) {
$this->serviceContainer = $serviceContainer;
}
}
The Dispatcher needs this service locator because thats how my routing works. If you use some extra mapping between routes and called methods, you could avoid this.
To inject something into other services I use the @inject
annotation:
/** @Inject({"@SomeLogic", "@SomeGateway"}) */
public function setDependencies(SomeLogic $someLogic, SomeGateway $someGateway) {
$this->someLogic = $someLogic;
$this->someGateway = $someGateway;
}
I like this approach because the configuration of a Service Class is done in the class itself and not in a separate file. Refactoring doesn't involve messing around with lengthy XML or YAML files.
To not harm the performance I run a script everytime I change some annotations and at build time (or in general on every vagrant up). It uses Symfony's ContainerBuilder and PHP Dumper to generate a PHP Lookup file which extends the Base ServiceContainer Class.
The lookup for the example dispatcher would look like this:
protected function getDispatcherService()
{
return $this->services['dispatcher'] = new \Some\Dispatcher($this);
}
This looks like a lot of work upfront, but it clearly separates concerns later on, especially in bigger projects. Classes do what they are supposed to do and need little to no non-domain-logic code at all.
And I try to keep factories to an absolutely minimum. The DI Container itself is one big factory and repository but it is not manually maintained.
EDIT:
My Routing Function looks like this, request_uri is something like Controller/action/...
$path = explode( self::ACTION_SEPARATOR, $request_uri );
if( empty( $path[1] ))
throw new RoutingException( 'Invalid Action' );
$method = ucfirst( $path[1] );
$className = $path[0].'Controller';
$qualifiedClassName = 'Some\\Namespace\\'.$className;
if( !class_exists( $qualifiedClassName ))
throw new RoutingException(sprintf('Invalid controller "%s" for route "%s"', $qualifiedClassName, $route));
if( !is_callable( array( $qualifiedClassName, $method ) ))
throw new RoutingException(sprintf('Invalid Action "%s"', $method));
$controller = $this->serviceContainer->get( $className );
call_user_func( array( $controller, $method ), $message );
It's perfectly fine - indeed, it's a good idea - to use dependency injection in your domain model.
You should to develop your domain model independently of any particular technology or library. The particulars of such-and-such database or so-and-so library are not the concern of your domain model: if you need to swap databases your business rules will remain the same. So we apply the dependency inversion principle and depend on interfaces which address the concerns of the domain model and adapters which adapt a given technological concern to the domain. (Importantly, the interface is phrased in terms of domain-level operations and lives in the same package as your domain classes.)
A great example of this is the Repository pattern. The Repository adapts the database to an interface which suits the domain model. So you can write customerRepository.FindByEmailAddress(email)
and not worry about the exact SQL statements that retrieve the customer for you. And by depending on an interface, your domain model has no idea whether it's a SQL database, a file, or MongoDB or Neo4j, or whatever.
So the Dependency Inversion Principle, and Dependency Injection, is of tremendous use in DDD. What I think is a less good idea is keeping your domain in an IOC container. The problem is that you end up storing domain-level information (like how to attach an order to a customer) away from your domain classes in a configuration file somewhere. IOC hides details that shouldn't really be hidden from your domain model. Also, the details of your particular container tend to leak into your domain classes.
Use IOC at the boundaries of your system, if you must, to wire up command handlers and other API services. Use Dependency Inversion and Dependency Injection in your domain model whenever it makes sense to do so, but keep IOC out of the picture.
Best Answer
It seems to me that you want to genericize the Head First example, so that it can accept an arbitrary collection of actions.
In C# you can have first-class functions, which means that you can pass functions to your finite state machine and have it maintain those functions in a list. Something like:
And then just pass your function to the AddStep method for insertion into the list by the state machine.
Then you just add your actions.