0

I understand the basic principles of dependency injection but would like some advice on how to handle classes instantiated within other classes methods; does this go against the dependency injection pattern?

Moreover, I would like to write the object (class) to a DB (mongo in object form) and so don't want it to be bloated with other dependencies.

To better explain what I mean here is an example:

Lets say we have a user class that gets injected with a server class -

class User{
    public $user_id
    public $user_name;

    public function __construct(server $server){
        $this->server = $server;
    }

    public function delete(){
        //...delete user code

        // Send mail to user
        $mailer = new mailer($this->server);
        $mailer->sendMail();
    }

}

So two things about this

  1. This means that the User class now gets bloated as it contains the server class also
  2. Does this break the Law of Demeter? As the User class doesn't actually need the Server class other than to pass it onto the Mailer class..

I understand a way around this would be to inject the mailer class into the delete function when called from an outside controller, meaning the User class never has to be injected with the server class:

$server = new server();
$mailer = new mailer($server);
$user = new User();
$user->delete($mailer);

class User{
    public $user_id
    public $user_name;

    public function __construct(){
        // other code
    }

    public function delete(mailer $mailer){
        //...delete user code

        // Send mail to user          
        $mailer->sendMail();
    }

}

But surely this means that you would need to know every class needed by methods within a class, if this nesting becomes a few levels deep surely this will be difficult to keep track of.

Also what happens if user->delete is a private method? You wouldn't be able to call it from an outside controller to pass in the mailer object in the first place.

So my question really is what's the best way of going about this?

Chris
  • 77
  • 6

1 Answers1

0

For me it is a smell to take a dependency on an object, just so you can construct a different object.

Instead take a dependency on the object that you want to construct instead. So in your case just pass a mailer to your Users constructor, then you don't need to create a mailer and you don't need to care about the server.

Here your User has a dependency on the mailer (to do the mailing) and so this is the thing that should be injected.

You objects should only be creating new instances of leaf objects/data holders/DTOs. Anything which provides any functionality (ie services) should be injected into the objects which need to make use of the functionality.

EDIT

I don't do PHP but I think your user class should look more like this:

class User{
    public $user_id
    public $user_name;

    public function __construct(mailer $mailer){
        $this->mailer = $mailer;
    }

    public function delete(){
        //...delete user code

        // Send mail to user
        $this->mailer->sendMail();
    }    
}

As for injecting via the constructor vs passing in to the method, this comes down to whether it is reasonable to expect a user of your User to provider a mailer when they want to delete a user. To me it doesn't sound like it is, so I would pass it through the constructor.

Chris
  • 77
  • 6
Sam Holder
  • 32,535
  • 13
  • 101
  • 181
  • Thanks. Didn't make it clear enough in my question but the mailer requires the server object – Chris Oct 17 '14 at 12:12
  • That is irrelevant to the `User` object. It shouldn't care if the `mailer` uses `Server` or anything, it just gets given a constructed, ready to use, `Mailer` object and uses that. How the `mailer` is implemented is down the the `mailer` to care about not the `User` – Sam Holder Oct 17 '14 at 12:14
  • Ok so similar to my second example of code but instead of passing the mailer object to the delete method I inject it into the User class on construct? Doesn't this mean that if you would need to keep track of every dependency every sub class uses? What happens if the nested class (mailer) has a class within one of its method that requires a dependency? – Chris Oct 17 '14 at 12:18
  • DI goes all the way down the rabbit hole. Any dependency your objects have they should require through their constructor. then your objects can only be created in a valid state. your mailer needs server so it should require a server in its constructor. Ideally you should create your object graphs and wire everything up in the [composition root](http://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection) of your applications. This will minimise the need to create objects everywhere in your code. – Sam Holder Oct 17 '14 at 12:22
  • Ok thanks, that makes sense a little better now. The only thing outstanding is how this will make the user class bloated. If I turn the User class into a json object for example, it will have all of the properties for mailer too, which aren't really needed... – Chris Oct 17 '14 at 12:34
  • The sense of your actual design is a separate question altogether :-). Should a User object really be responsible for sending an email when it is deleted? Probably not IMHO. You probably want a service whose job it is to manage your users and this should have the method to delete the user (and it can then send the email). You users should have methods an properties related to a user, not related to *managing users* – Sam Holder Oct 17 '14 at 12:38
  • A user is more likely just a functionless bag of properties (like a DTO). this can then be turned into json without any worries. you should not be sending objects which have behaviour across the wire as often the behaviour is difficult to maintain when the object is deserialised (like where will the mailer come from in that case?) – Sam Holder Oct 17 '14 at 12:41