3

I've been reading about Dependency Injection but the examples I've found just look like bad code to me, so my main question is am I right in thinking it is bad code, or am I misunderstanding the purpose of it, and is my example any better?

class Photo {
   protected $db;
   public function __construct()
   {
      $this->db = DB::getInstance();
   }
}

So this is bad code, and the suggestion of Dependency Injection, due to the multitude of setters that might be created if we explicitly set every variable, is:

class Container {
   protected $db;
   public static newPhoto()
   {
      $photo = new Photo;
      $photo->setDB(static::$db);
      $photo->setConfig();
      $photo->setResponse();
      return $photo;
   }
}
$photo = Container::newPhoto();

But correct me if I'm wrong, we've just built a class whose sole responsibility it is to build another class, seems quite pointless, and we are using static methods which is apparently a very bad idea.

The one benefit that I do see, which surprisingly to me isn't mentioned is that we can now test the Photo class independently by using the setters, whilst in the first example we couldn't.

Something like this makes more sense to me:

class Photo {
   protected $db;
   protected $config;
   protected $response;
   public function __construct($dbConn=null,$config='123',$response=true)
   {
      if(is_null($dbConn))
          $this->db = DB::getInstance();
      else
          $this->db = $dbConn;
      ...etc
   }
}
$photo = new Photo($dbConn);

The class builds itself, there is no need for the static method to actually be called, the class can be tested with dummy data if values are used otherwise it falls back on defaults (which seems to be the only point of the Container class), and the dependencies are still somewhat obvious as opposed to the Container.

mrmryb
  • 1,479
  • 1
  • 12
  • 18
  • 1
    By my count, this "question" contains 6 different questions. Why? Did SO start asking money for posting questions while I was sleeping? – tereško Jan 18 '13 at 21:08
  • @tereško It may contain multiple smaller questions but the questions are all about the same thing, so why is this a problem? The questions are there to clarify my thoughts on the topic, which are all about Dependency Injection, so even without answering all of my questions people are free to explain the benefits of one piece of code over the other and still answer my overall question. If i simply said please explain the benefit of this code over the other people could use multiple reasons in their answer, that does not mean they are all separate answers but small parts of an overall answer. – mrmryb Jan 18 '13 at 21:14
  • and which would be the "over all question"? – tereško Jan 18 '13 at 21:17
  • @mrmryb, please [review the site FAQ](http://stackoverflow.com/faq#dontask): "Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much." While you aren't asking for a book, the scope of your question is rather large, which makes getting a single, coherent *answer* difficult. – Charles Jan 18 '13 at 21:17
  • Essentially, read the [PoEAA](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) or learn to write questions. – tereško Jan 18 '13 at 21:18
  • My overall question is whether the code I am confused about is bad code, because of the reasons I provided, or whether I am misunderstanding it's purpose. Apologies for not making it clearer and I will edit the question for that aim. @tereško you could learn to be a bit more constructive – mrmryb Jan 18 '13 at 21:19
  • 2
    Yes, your code is bad and you have not understood what DI is. Do not edit this wall of text. Make a separate question for each part. – tereško Jan 18 '13 at 21:24
  • @tereško Wow, you really are very unhelpful. I will edit the text to make my question clear, I don't see what your problem with that would be. If my code is bad, edit your answer to tell my why, that is all I am asking. – mrmryb Jan 18 '13 at 21:27
  • @tereško - I have to agree: your comments are unhelpful, unconstructive and somewhat rude. And your answer was terse to the point of being impenetrable. If the question is *that* bad, just vote to close and forget about it. – Spudley Jan 18 '13 at 21:50
  • Most of this wouldn't be a problem with a good DI framework. – ta.speot.is Jan 18 '13 at 21:53
  • @mrmryb - there's a good chance this question will end up being deleted, so if you want to hang onto any of the answers, you might want to copy+paste them somewhere safe. :-) – Spudley Jan 18 '13 at 21:55
  • @ta.speot.is , "dependency injection" is a methodology, not a framework. What you are thinking of is DIC, which in PHP is more of a problem then a solution. – tereško Jan 18 '13 at 21:58
  • I have edited the question greatly to focus on the two pieces of code that are causing me confusion, I would be grateful to get my question reopened and get some more specific answers, but if my question is still not constructive I'd like to know why. – mrmryb Jan 18 '13 at 22:06

2 Answers2

2

The goal of Dependency Injection pattern is to decouple how objects that work together are constructed. In your example, it is not the concern of the Photo class to know how to construct a DB instance, but only to use a DB instance to achieve its goals.

The obvious advantage that you already noticed is in testing where you can easily pass mock DB instance if you want to test just the Photo functionality. But you can think also about connection pooling for example where a container has a pool of DB instances and pass one of them to your Photo object for doing its work. When the Photo life-cycle ends the DB instance is returned to the pool and used elsewhere.

The implementation of this pattern can be achieved using constructors with arguments, setters, annotations (at least in Java) and even XML configuration files. In the case of annotations or XML configuration files a container will parse that information create the appropriate needed objects and inject them in the client class.

What you describe at C1 and C2 is a factory class that exposes static methods for obtaining Photo instances. This is a very common pattern used in many places in Java.

Bogdan
  • 934
  • 7
  • 13
1

I don't know where you leaned about the DI, but the resources seem quite questionable. Instead you should begin by watching this lecture, followed by article from Martin Fowler. Maybe supplemented by this short video.

class Container {
   protected $db;
   public static newPhoto()
   {
      $photo = new Photo;
      $photo->setDB(static::$db);
      $photo->setConfig();
      $photo->setResponse();
      return $photo;
   }
}
$photo = Container::newPhoto();

This is not implementing dependency injection. It actually is just an example of poorly implemented static factory method (anti)pattern. Especially the magical methods $photo->setConfig() and $photo->setResponse() which apparently do some work, but receive no parameters.

And then there is this code:

class Photo {
   protected $db;
   protected $config;
   protected $response;
   public function __construct($dbConn=null,$config='123',$response=true)
   {
      if(is_null($dbConn))
          $this->db = DB::getInstance();
      else
          $this->db = $dbConn;
      ...etc
   }
}
$photo = new Photo($dbConn);

Instead of just assigning values, you decide to hide the dependencies of it, and, if they are not provided, to take them from global scope. And of course, your constructor ends up containing quite a log of computation. Thus making it untestable.

Oh .. and then there is the magical boolean value. Whenever where is such a parameter assigned to a class in constructor, it is a clear sign that you actually needed two different classes, which implement same interface.

So.. to answer your question:

I've been reading about Dependency Injection but the examples I've found just look like bad code to me, so my main question is am I right in thinking it is bad code, or am I misunderstanding the purpose of it, and is my example any better?

No. Your example is not better. You just combined the worst parts from both your earlier code example in single class definition.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Genuinely thank you. I have looked at a few places but the example I got was from [Nettuts+](http://net.tutsplus.com/tutorials/php/dependency-injection-huh/) which seemed trustworthy, that's why I was so confused because it seemed so bad. I dont understand this though: 'you decide to hide the dependencies of it' - it seemed to me that I was not hiding them because I could do `$photo = new Photo($dbConn,$config,$response);` whilst retaining the default values that are provided by the first example. Better to leave out and empty the construct? What would you do in this situation for proper DI? – mrmryb Jan 18 '13 at 23:10
  • First of all, there `$response` parameter should not even be there. It is a *FLAG*. It would select for two different behaviors. Instead, you should have two polymorphic classes, or extract that behavior to the class which uses `Photo`. Watch the lecture (and others from same series), they will explain some of the issues there. – tereško Jan 18 '13 at 23:16
  • As for constructor's parameters. It is ok to have default values for the,. But "what to do" which such situation should not be handles in constructor. If you see that you class needs to be "prepared" before it becomes available in the rest of the code, then use a `Builder` for it, which would then call `prepare()` or `init()` method in the created instance. Thing is, that in this example the DB connection is **mandatory**. Your class cannot function without it. You should not even need to do any checks here. If DB instance is not provides, it is a fatal error. – tereško Jan 18 '13 at 23:18
  • Oh .. and don't looks for good practices on Nettuts+. The average quality for articles there is extremely poor. There are few decant once, but the page the target audience of that site would not know the difference. – tereško Jan 18 '13 at 23:23
  • I never thought about it that way, that if $db isn't provided it should error out rather than just carry working, but it definitely makes sense now that you mention it. I will remember to stay clear of Nettuts in the future, they have only led me to confusion. But at least I have some clarification now. Thanks. One more thing, what do you mean by a `Builder`? Is it similar to how I shouldn't call for $db in the class, I should create separate classes for any preparation and inject the values I need? – mrmryb Jan 18 '13 at 23:30
  • *Builder* basically is a type of *Factory*, that only produces one class of objects. I don't really have any good examples at hand. The best I could find are [this](http://stackoverflow.com/a/8278581/727208) and [this](http://stackoverflow.com/a/11369679/727208). The code fragments there should be enough to illustrate the difference between factory and builder. It also shows how to use factory to provide all a set of classes with same instance of DB connection. – tereško Jan 18 '13 at 23:48