Dependency Injection Breaks Encapsulation

I never made any such assumption. My only assumption is that the requirements might change and building the application in such a way that I can easily handle that is better than making the assumption that the requirements will never change.

That said, I do want to make my code reusable. I want to be able to use the same code on hundreds of projects if possible because it will save me time. I can only do that if:

  • The requirements on the projects are identical (very unlikely!)

OR

  • I build the code in such a way that it can easily handle requirements differences between the projects.

OR

  • I create a different branch of the code for each project. However, this causes problems if I want to backport features/bugfixes to earlier projects as the code essentially has been copied, pasted and modified (breaking DRY essentially) rather than reused.

I have given my reasons, but you continually choose to ignore them.

Where are these “best practices” published?

I disagree. I have pointed out several times in this discussion where I believe DI is a good fit. You should remember that DI, as with every other design pattern or technique, is a particular solution to a particular problem or situation. If you have that particular problem or situation, then that solution could be a good fit. But where you do not have that problem or situation, then implementing that particular solution will not be a good fit. Not only are you inserting code to cover a situation that will never be encountered, you are also making your code base more bloated, more fragmented, more difficult to read and understand, and therefore more difficult to maintain.

The problem for which DI is the solution is where an object has a dependency which can be satisfied from a number of alternatives. You can either put the code which decides which of the possible alternatives to use inside the object, or you can make the decision outside the object and inject it into the object. This means that the object does not have to decide which dependency to use, it simply uses what it has been given.

If you have the situation where an object has a dependency which fixed and does not have any alternatives, then this falls outside the situation for which DI was designed, so would not be a good fit.

This is why I use DI where a dependency can be supplied from a number of alternatives, and I do not use DI where a dependency is fixed and does not have any alternatives.

The idea that DI should be universally applied regardless of the circumstances is just plain wrong as far as I am concerned. The following quote is taken from http://web.archive.org/web/20130516130335/http://theruntime.com/blogs/jacob/archive/2007/12/10/dependency-injection-objection.aspx

[quote]
The claim made by these individuals is that The Pattern (it can be any pattern, but this is increasingly frequent when referring to Dependency Injection) is universally applicable and should be used in all cases, preferably by default. I’m sorry, but this line of argument only shows the inexperience or narrow focus of those making the claim.

Claims of universal applicability for any pattern or development principle are always wrong. [/quote]

There is a difference between claiming something to be best practice and universally applicable. All I’m saying is that by definition, a piece of software that follows best practices is better designed than one that does not. Saying “I don’t want to use DI here” is perfectly fine however, claiming that makes it better designed, where “better” is the the set of a subset of the traits I posted above is demonstrably wrong.

This simply isn’t true though is it? I’ve demonstrated multiple times that DI in this scenario adds flexibility at zero cost. Flexiblity is desirable: http://digital-library.theiet.org/content/journals/10.1049/ip-sen_20050045 the cost of implementing DI is zero and the benefit is huge… this isn’t a reason not to use DI. You can use an alternative, but why would you? and why would you? is a question you seem incapable of answering beyond “I don’t want to”.

I pointed this out in post #91:

And you still just don’t get it. To say “DI isn’t the right tool” you have to show that another tool, by some metric is actively better.

Flexibility has been recognised as a desirable quality of software since the earliest days of software engineering. Classic and contemporary software design literature suggests that particular implementations are more flexible than others,

There is simply no debate about whether or not flexibility is a good thing or not in terms of application architecture. There may be cases where flexibility causes a performance overhead that is undesirable, however, that is not an issue here as you wouldn’t be using PHP (and possibly not OOP) if that was the case, and in the case of DI vs singleton, DI is faster anyway.

[quote=“TomB, post:249, topic:113596”]
There is a difference between claiming something to be best practice and universally applicable.[/quote]
But where are these “best practices” documented?

I disagree. If a novice follows a set of rules which have been written by a set of experts there is absolutely no guarantee that the novice’s result will be of the same quality as that produced by those experts.

You obviously did not read that quote from Craig Larman which I included in an earlier post. It went like this

Note this particular sentence [quote]Experts choose with insight - perhaps choosing a simple and brittle design whose cost of change is balanced against its likelihood.[/quote]
To me this means that I am allowed to implement what looks like a brittle and imperfect design if I judge that the cost of any unlikely changes will still be less than the cost of making the design less brittle and more perfect to begin with. As an example, if the cost of implementing flexibility is £100, but over a five year period I have had to change my “brittle” design at a cost of only £50, then my decision has saved £50. My decision was the most cost-effective.

I believe that it is. It’s simple logic - if a particular solution is for a particular problem, but you don’t have that particular problem, then what does implementing that solution solve?

Just because you say there is zero cost does not make it so. And what is the point of introducing flexibility if you are never going to use that flexibility? The simple act of taking a few lines of code that could exist in a single place and scattering them over several places results in fragmented code which is harder to write, harder to read, harder to understand, and harder to maintain. So you end up with the cost of writing code in several places instead of one place, and with negative benefits. That is not a good choice in my book, so it is not a choice that I am willing to make.

Because the alternative is quicker, cheaper, and easier to maintain.

No I don’t. I will continue to use a quicker and cheaper alternative unless I actually have the situation which DI was originally designed to be of assistance.

I disagree. You have to balance the cost of implementing that flexibility with the likelihood that you are actually going to need it. See my earlier quote from Craig Larman on this very issue.

But again, the cost of implementing DI is generally zero… so the point here is moot.

I disagree. You have to write that code somewhere. When you instantiate an object you have to write “new X()”. Changing that to “new X(new Y))” is no more difficult or work. If you use a singleton then you have to write the code for singleton class before you use it: Extra work… and as I showed in #235 you have to write an extra line of code in every single method you want to use the dependency again, repeating yourself. Again, more work.

[citation needed].

Once again I point you to posts #235 where I demonstrated that your method means repeating yourself and the 5+ posts in this thread and the last where I demonstrated repeatedly that your method was actively more code. How exactly is writing more code quicker and cheaper? Please stop making this point unless you can actually back it up with something because this “NO IT’S NOT” argument is stupid.

As for “easier to maintain”. That metric is heavily tied to flexibility, something you claim not to want. Your goals here are contradictory.

I’ve linked to literally dozens of sources throughout this. Pages from Academic Journals, MSDN, IBM, well respected programmers blogs. Essentially: people who know a hell a lot more about this stuff than you do. If you scroll back you’ll find a lot of interesting stuff to read.

It’s worth taking a step back and thinking why things get labelled “good practice” and “bad practice”. Things get labelled “bad” because people have tried an approach and then found problems with it and highlighted approaches that solved the problems they encountered. Bad practices are labelled bad because they introduce traits which have been (at this point) extensively documented as being a bad thing in software design. Some of these are Tight Coupling, Action at a distance, poor separation of concerns and broken encapsulation. As these problems are demonstrably bad, we can analyse two solutions against these metrics and accurately deduce that the one that introduces the fewest negative traits is better to use.

Tony, I asked you this repeatedly in the last thread. This discussion is utterly pointless unless you can provide a use case where DI is demonstrably inferior. 500+ posts later and you still cannot seem to provide this.

To do this, you need to provide:

  • A (complete, minimal, self-contained) code example where you are using DI to achieve a goal
  • A (complete, minimal, self-contained) code example where you using another approach that meets the same goal that you claim an improvement has been made over the DI version of the code
  • A brief explanation of the metric you are using to define “improved”.

Until you do this, your argument is moot and no amount of shouting about definitions to quoting me out of context is going to help you make your point.

Hey everyone,

Let’s all play nice so this thread stays open. No personal attacks or insults. Keep it relevant to this thread.

I’d like to say, (despite saying I’d leave the conversation) great list and thanks for it! I am going to make a poster of it and hang in on the wall. :smiley:

Scott

I’ll try to exemplify my concerns with real experiences of my own. Again, I’m not against Dependency Injection or any other pattern. I defend the opposite of this, but always with a concern of over abusing when there’s “no need for it” or “forbides me”. But let’s go to the examples:

##Pont one: too much abstraction (with some narrative for fun)##

A long long time ago… in a not so distant desk on an UK office, a boss asks me to implement a new Person entity and to calculate a “slug” for it. For simplicity of the examples, I’ve implemented it in the __toString and just concatenate everything, but let us make the abstraction for a second and imagine a real business class.
So, the first though is to do a dirty and ugly approach. Implement everything in that business class.

class Person
{
  private $name;
  private $dateOfBirth;
  private $address;
  private $portNumber;

  function __construct($name, $dateOfBirth, $address, $portNumber)
  {
    $this->name = $name;
    $this->dateOfBirth = $dateOfBirth;
    $this->address = $address;
    $this->portNumber = $portNumber;
  }

  public function __toString()
  {
    return sprintf("%s - %s - %s - %s",
      $this->name, $this->dateOfBirth->format("Y-m-d H:i:s"), $this->address, $this->portNumber);
  }
}

$p = new Person('Mr. Batman', new \DateTime("today"), 'Somewhere', '10');
echo "Assertion result => " . (assert("Mr. Batman - 2015-03-05 00:00:00 - Somewhere - 10" == $p) ? "true" : "false") . "\r\n";

Hum… but I thought "This has too many responsibilities and one might need to change the slug or the Person. But then, maybe, in the future my business class may have the need to calculate the slug in another way, and having the sprintf hardcoded isn’t good, so let’s make a dependency out of it…
Ok, just need to create a new file with a formatter class, arrange it in the appropriate namespace (imagine), make some tests for it (another class) and have some getters for my members in my business class… no big deal… "

class Person
{
  private $formatter;

  private $name;
  private $dateOfBirth;
  private $address;
  private $portNumber;

  public function getName()
  {
    return $this->name;
  }

  public function getDateOfBirth()
  {
    return $this->dateOfBirth;
  }

  public function getAddress()
  {
    return $this->address;
  }

  public function getPortNumber()
  {
    return $this->portNumber;
  }

  function __construct($formatter, $name, $dateOfBirth, $address, $portNumber)
  {
    $this->formatter = $formatter;
    $this->name = $name;
    $this->dateOfBirth = $dateOfBirth;
    $this->address = $address;
    $this->portNumber = $portNumber;
  }

  public function __toString()
  {
    return $this->formatter->format($this);
  }
}

class PersonFormatter
{
  private $format;

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

  public function format(Person $p)
  {
    return sprintf($this->format, $p->getName(), $p->getDateOfBirth()->format("Y-m-d H:i:s"), $p->getAddress(), $p->getPortNumber());
  }
}

$pf = new PersonFormatter("%s - %s - %s - %s");
$p = new Person($pf, 'Mr. Batman', new \DateTime("today"), 'Somewhere', '10');
echo "Assertion result => " . (assert("Mr. Batman - 2015-03-05 00:00:00 - Somewhere - 10" == $p) ? "true" : "false") . "\r\n";

"Yes. Much better, now I have a Person entity and I can replace my slugger at any time!
Hum… wait! I really don’t like that I’m stuck with formatting the dates with my locale’s machine. Probably in the future this will be deployed in a Portuguese computer and the system will need to compute “Segunda-feira” instead of “Monday”. What I need is to have a DateFormatter… And because this is easy… why not?
Ok, just need to create a new DateTimeFormatter passed the locale (imagine this), have a new bunch of tests for each of them, and change the PersonFormatter. "

    class Person
    {
      private $formatter;
    
      private $name;
      private $dateOfBirth;
      private $address;
      private $portNumber;
    
      public function getName()
      {
        return $this->name;
      }
    
      public function getDateOfBirth()
      {
        return $this->dateOfBirth;
      }
    
      public function getAddress()
      {
        return $this->address;
      }
    
      public function getPortNumber()
      {
        return $this->portNumber;
      }
    
      function __construct($formatter, $name, $dateOfBirth, $address, $portNumber)
      {
        $this->formatter = $formatter;
        $this->name = $name;
        $this->dateOfBirth = $dateOfBirth;
        $this->address = $address;
        $this->portNumber = $portNumber;
      }
    
      public function __toString()
      {
        return $this->formatter->format($this);
      }
    }
    
    class DateTimeFormatter {
      private $format;
    
      public function __construct($format) {
        $this->format = $format;
      }
    
      public function format(\DateTime $theDate) {
        return $theDate->format($this->format);
      }
    }
    
    class PersonFormatter
    {
      private $format;
      private $dateTimeFormatter;
    
      public function __construct(DateTimeFormatter $dateTimeFormatter, $format)
      {
        $this->dateTimeFormatter = $dateTimeFormatter;
        $this->format = $format;
      }
    
      public function format(Person $p)
      {
        return sprintf($this->format, $p->getName(), $this->dateTimeFormatter->format($p->getDateOfBirth()), $p->getAddress(), $p->getPortNumber());
      }
    }
    $dtf = new DateTimeFormatter("Y-m-d H:i:s");
    $pf = new PersonFormatter($dtf, "%s - %s - %s - %s");
    $p = new Person($pf, 'Mr. Batman', new \DateTime("today"), 'Somewhere', '10');
    echo "Assertion result => " . (assert("Mr. Batman - 2015-03-05 00:00:00 - Somewhere - 10" == $p) ? "true" : "false") . "\r\n";


"Cool! Didn’t even need to change the `Person`! Hurray, it’s decoupled! But… the `Person’s` entity name in the future could be composed by a title, name and surname. Maybe what I need is to replace with yet another dependency. Just need to create a few more classes, have proper getters, a few more tests, change the `Person` constructor et voilá."

class PersonIdentification {
  protected $name;

  public function getName() {return $this->name; }

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

  public function __toString() {
    return $this->name;
  }
}

class Birthday {
  private $formatter;

  private $dateOfBirth;

  public function getDateOfBirth() { return $this->dateOfBirth; }

  public function __construct(DateTimeFormatter $formatter, \DateTime $dateOfBirth) {
    $this->dateOfBirth = $dateOfBirth;
    $this->formatter = $formatter;
  }

  public function __toString() {
    return $this->formatter->format($this->dateOfBirth);
  }
}

class Location {
  private $address;
  private $portNumber;

  public function getAddress() { return $this->address; }
  public function getPortNumber() { return $this->portNumber; }

  public function __construct($address, $portNumber) {
    $this->address = $address;
    $this->portNumber = $portNumber;
  }

  public function __toString() {
    return $this->address . " - " . $this->portNumber;
  }
}


class Person
{
  private $formatter;
  private $personIdentification;
  private $birthday;
  private $location;

  public function getPersonIdentification() { return $this->personIdentification; }
  public function getBirthday() { return $this->birthday; }
  public function getLocation() { return $this->location; }

  public function __construct($formatter, PersonIdentification $personIdentification, Birthday $birthday, Location $location) {
    $this->formatter = $formatter;
    $this->personIdentification = $personIdentification;
    $this->birthday = $birthday;
    $this->location = $location;
  }

  public function __toString() {
    return $this->formatter->format($this);
  }
}


class DateTimeFormatter {
  private $format;

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

  public function format(\DateTime $theDate) {
    return $theDate->format($this->format);
  }
}

class PersonFormatter
{
  private $format;

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

  public function format(Person $p)
  {
    return sprintf($this->format, $p->getPersonIdentification(), $p->getBirthday(), $p->getLocation());
  }
}
$dtf = new DateTimeFormatter("Y-m-d H:i:s");
$pf = new PersonFormatter("%s - %s - %s");
$b = new Birthday($dtf, new \DateTime("today"));
$pi = new PersonIdentification('Mr. Batman');
$l = new Location('Somewhere', '10');

$p = new Person($pf, $pi, $b, $l);
echo "Assertion result => " . (assert("Mr. Batman - 2015-03-05 00:00:00 - Somewhere - 10" == $p) ? "true" : "false") . "\r\n";

“What if……” and then my boss arrives and asks me… “Have you finished? Why did you took 10 hours do a simple Persons class and a slugger.?" And I answer, “Yes, it took me a while, but my implementation is ready to be deployed in a new country and to calculate the slug in any way you would like!” The boss answer “What!? Did you know that this “liberty” costs me 400$ of your time!?”. You know what happens next…

Besides the narrative, my point is, you have to know where to stop. In the current context I would probably stop at the second stage. Because that was the perceive limit of the value of the next line of code, beyond that for the companies profile, it was just “garbage”. If, on the other hand, the company produces software to other countries, than I would probably proceed one step further.
I must know that each line of code is going to brings value to the customer, as he might not understand what “tomorrow” is (there is a point for this at the bottom), even if I demonstrate it with previously examples. Otherwise, you’ll make a dependency out a int because it could turn out to be a 256 bit number in the future. And yes, I’ve seen this happening on a subcontract application!

##Seconds: Readability##

Either you be an expert or a junior I think that there’s no question that the first example above is easier to read than the second. All my business and context is there, in one file, in one class. The question is, if I’m the guy that is in a hurry to fix a bug because the functionality must be online in about 2 minutes, and someone forgot to write a test for it, then, in this scenario, if I was the wrench guy, would prefer the first one.
If the program was just handled to me, without any context, because the guy was fired, and I needed to evaluate the flow of a program, again, the first one is easier to read, because I’ve got all the context of it.
Again, I’m not defending, in this context, the first dirty solution, just showing some of the advantages of it. Will they overcome every other benefits? That will depend on a number of factors that might even include the history and pattern of implementation of the application itself.

##Third: Resources (blame DI Containers!)##

As someone mention before, DI can have some minor performance penalty, and I’ll try to demonstrate with an example from your own research.
Dependency Injection is almost associated with a container and there quite a few plenty of container to choose from. Each has some advantages and disadvantages and its own learning curve. Now, as you mentioned in the article http://www.sitepoint.com/php-dependency-injection-container-performance-benchmarks/, depending of the container you’ll have a dependency resolution time and memory profile, which might be a concern. For instance, if I’m on a cloud I want to minimize all of these to save me costs. Of course, if you really want performance and control, you can build your own DI Container, or choose another language like C or C++, but even in PHP I could be using HHVM and have that performance boost. And having to know another language just because of it could be a wrong move.
The problem is not the DI by it self, but the chosen DI container.

Forth: Dependency Resolution

You’ve mentioned before that you postpone every single dependency of your classes to the entry point of the application. Let me take you to a simple example. I’ve create a simple REST project base on Standard Symfony distribution with 7 external bundles and I’ve got three more bundles of my own (JMSSerializerBundle, FOSUserBundle, DoctrineFixturesBundle, FOSOAuthServerBundle, FOSRestBundle, NelmioCorsBundle, StofDoctrineExtensionsBundle)
Roughly there are 372 exposed services, with, I don’t even what to calculate, thousands of constructor parameters. And those are the exposed services, because there plenty more classes instantiated in each module. Can you imagine if no bundle gave you the “services.yml” and you have to setup the dependency graph on your own (with or without using a DI Container). The amount of documentation that you need to read would be tremendous, not to say that you’ll loose the ability to simply do

new JMS\SerializerBundle\JMSSerializerBundle(),

to use the JMSSerializer bundle.
From what I’ve understand, is that you turn almost every class (except for those that are implementations of abstractions) into a dependency to be resolved in the entry point. For the bundles that I mentioned early, there are 12000 php files, even if there is 10% of classes/interfaces that are dependencies (and the rest of them are implementations), there would be 1200 files to graph. Maybe I just don’t understand what you mean. If this isn’t what you intent to say can you rephrase? If it is, how do you setup a project like this?
Having a module, predefine its own dependency resolution gives me the power of:

new JMS\SerializerBundle\JMSSerializerBundle(),

Fifth: The benefits costs

Not all customers evaluate the benefits that we programmers do. If all of these items, that you correctly mentioned, Robustness, Maintainability, usefulness, scalability, …, will cost 20% more, you’ll probably lose the customer. This is actually pretty easy to see the number of projects that are won, just because they are cheaper.
The sponsor might even understand those benefits on the long run, but it just might not have the extra 20% bucks. So, we are again, stuck.
Again, there are things that need to address daily, whenever I need to budget a project. I need to balance the customer needs with the resources that I’ve got available.

##Conclusion… for now.##
These are real examples, lived by me, shown with very simple examples Some of them are a daily obstacle others are extreme cases, but I’ve experience pretty much all of those.
Again, nothing that I’ve mention above is to be read as “good practices are bad". But whenever we encountered an obstacle, we need to know which way is the best to address it. And, sometimes, the ugly way could be your best choice.

Thank you for your time

1 Like

But this is a very different topic. This is known as Premature Abstraction ( http://c2.com/cgi/wiki?PrematureAbstraction ). You have counted the time for both refactoring and initial development here. Apples to apples you have to consider the time it would take to write either from scratch which is going to be pretty much the same.

You’re comparing the time it takes:

  • To create the initial person class with poor separation of concerns

To the time it takes to:

  • Create the initial person class with poor separation of concerns
  • as well as the time it takes to refactor that initial design flaw out

The bigger issue with this example, however, is that it’s fundamentally apples-to-oranges. In the first example you have no dependencies, in the second one you add one.

To make this a fair comparison you need to change the first example to be this:

class Person
{
  private $formatter;

  private $name;
  private $dateOfBirth;
  private $address;
  private $portNumber;

  public function getName()
  {
    return $this->name;
  }

  public function getDateOfBirth()
  {
    return $this->dateOfBirth;
  }

  public function getAddress()
  {
    return $this->address;
  }

  public function getPortNumber()
  {
    return $this->portNumber;
  }

  function __construct($formatter, $name, $dateOfBirth, $address, $portNumber)
  {
    $this->formatter = new PersonFormatter();
    $this->name = $name;
    $this->dateOfBirth = $dateOfBirth;
    $this->address = $address;
    $this->portNumber = $portNumber;
  }

  public function __toString()
  {
    return $this->formatter->format($this);
  }
}

I agree that this is a separation of concerns issue, and I think your solution is valid, but highlighting an issue with SoC and comparing that to adding a whole new dependency is not a straight comparison.

Seconds: Readability

That really is another issue of perspective. If I’m looking at the entry point of the program and see:

new Person();

And I want to change the date format being used, I look into the person class and see

$this->formatter = new PersonFormatter();

So now I have to go and open the PersonFormatter class

If that then contains something like

$this->config = new Config();

//...

$date->format($this->config->dateFormat);

I now have to keep digging into each file one at a time until I actually find the code I want.

On the other hand, If I look at the entry point and see:

new  PersonFormatter(new Person());

or even

new PersonFormatter(new Person(), 'd/m/Y');

I have significantly fewer files to look though to change the date format.

As I mentioned earlier on, if you’re worried about a script running in 0.002 seconds instead of 0.001 seconds then you probably shouldn’t be using PHP or OOP in the first place. Not that it’s relevant, you don’t have to use a DI container to use DI so the point here is really not relevant to the discussion at hand because again you’re comparing apples to oranges.

This is a bit of a red herring. When using DI there is nothing wrong with a factory method that provides a preconfigured instance. Consider your formatting example:

$dtf = new DateTimeFormatter("Y-m-d H:i:s");
$pf = new PersonFormatter($dtf, "%s - %s - %s - %s");

There’s no reason that you can’t move this logic into a factory method:

function createPerson($name, \DateTime $date, $address, $portNumber) {
$dtf = new DateTimeFormatter("Y-m-d H:i:s");
$pf = new PersonFormatter($dtf, "%s - %s - %s - %s");
return new Person($pf, $name, $date, $address, $portNumber);
}

$p = createPerson('.....');

The difference between this and having everything created inside new Person() (so Person constructs the PersonFormatter and PersonFormatter constructs DateTimeFormatter) is that if I need to I can create another method that uses a different date format without breaking any existing code or having to dig in to the person/dateformatter/etc classes.

Again, it’s separation of concerns issue. The concern here is dependency resolution, which should not be a concern of the Person class.

I’m not sure I agree with this. As your example of time taken wasn’t really a fair comparison it’s difficult to suggest that creating well structured software costs more. Requirements Engineering is incredibly difficult and even with the best Requirements Engineers and the most helpful clients the requirements tend to change between the requirements being analysed and the product being released.

I’ve had many situations where we’ve built something exactly as the client asked, only for them to say “That’s good, but now that I’ve seen it working I actually need it to do something else” or “That’s great! Can you also make it do whatever”. This kind of thing happens all the time and the benefit to the client of being able to quickly change the way something works is pure time/money. Not only do they get the finished project earlier, but they get it at a lower cost.

1 Like

Is it? Remember that I’m talking in a simulated business scenario, and event in that scenario, if the company develops multilanguage apps, having a DateFormatter could be useful. But, at that time it didn’t bring value to the company, but in a future it might. How do you weight the might?

Also, for me Dependency Injection is connected to Abstraction. Is all about “Right now I’ve got this class but maybe tomorrow I’ll have another” (abstraction). If you abstract to much, I’ll get yourself into the situation that I’ve presented. The thing is: You always have “hard” dependencies. Either it be an int, string, the php engine, the machine architecture or an interface that you built to get that abstraction. And even those interfaces might need to “change” and you might do some BC (or create yet another abstraction if required to do so)

And sorry, I can’t possible imagine that you would take the same amount of time writing all of those scenarios. The amount of classes, LoC, constructor arguments, is quite different, even adding the getters as you mentioned, I’ve got:

Version 1 - 30 Loc - 109 Chars - 1 classes/files
Version 2 - 48 Loc - 143 Chars - 2 classes/files
Version 3 - 62 Loc - 176 Chars - 3 classes/files
Version 4 - 108 Loc - 280 Chars - 6 classes/files

Do you mean that, for this requirement, you write faster with DI in mind? Remember, I don’t question all other benefits in a possible long run, but you need to weight the possibility that the future might not need it.

What if you have a null reference exception in your business class because some dependency object was missed behaved? What implementation of the dependecy caused that error. You’ll have to debug in order to see what it was or to get your DI tree to figure it out. But you might be in troubble, because it might only break in production (no debug can be done), you’re providing a library for others to use, (the ones that are complaining).


As you may read, I’ve mentioned right in the beggining. But, I haven’t seen a complex project that did do DI without a container, so in practice, they come hand in hand. As you mentioned in your article just doing this:

for ($i = 0; $i < 10000; $i++) {
    $j = new J(new I(new H(new G(new F(new E(new D(new C(new B(new A())))))))));
}

with Zend\Di took 37 seconds to resolve.
Yes, I can theoretically disregard a container, but I don’t do theoretically software.


Yes, because you’ve mentioned:

in reply to my comment:


The problem is defining the well structured program. Is it always following the best practices. As I demonstrated above with real scenarios that happend to (although with simplified examples), the line that defines well, best, optimal is subjective. Even in accounting a 100M€ profit can be excellent when presenting to the stockholders and be bad when presenting reasons why the boss doesn’t give you a raise.

I know… I’m circulating around…my point is, “There is no point!”. There can be various points of views and yours is one, and I respect it. Honestly, I didn’t see that from your behalf.

Regards,
Sérgio Amorim

You missed my point though, you can’t use that as an argument against DI. I agree with you and separation of concerns is a very big topic in in itself but you’re describing a problem with the initial code then comparing a solution to that problem to the problem itself, not a solution to the problem with another solution to the problem which is the only way to get a fair comparison.

The question here is “Would this code be improved by separating out the display logic?” the answer in most cases will be “Yes”. So the question you now need to ask, if you have determined that the code would be improved by separating out the display logic then the question becomes “Which method of doing that is preferable?” not, as you are, “Is this solution to the problem more code than the original problem”.

The difference is subtle but important. If you have determined that you need to separate out the display logic then you can look at different methods of doing that, but conflating the initial problem with one possible solution is not helpful in this discussion.

This is true but it’s not really relevant, you don’t generally hard code primitives, you use variables which are set in the constructor. Saying “The first argument is an Integer” is no different than saying “The first argument is an Address object”. There’s no difference here. And on-topic, I can define an argument as a float and accept an integer, I just need some kind of numeric value, most programming languages will allow this basic form of primitive polymorphism by doing the conversion for you. PHP and other loosely typed languages do this to a greater extent, I can pass “123” as a string into an argument that requires an int and it will just work.

I’m not sure I understand this. If you get a bug that exists only in production then your testing is sub-par but that’s a whole different issue.

As I said before, if someone is complaining that they passed an invalid argument it’s their own fault. You’re essentially saying people are complaining when they do this:

abs("ABCDE");

And then complaining when it breaks which is clearly user-error.

If you’re providing a library for others to use, loose coupling is even more important because unless the requirements for their project are exactly the same as yours then they won’t be able to use your library. If they can inject the dependencies themselves, they can use at least some of your code.

But by the same token in reality and out side these theoretical examples requirements change often. Let’s take the code you quoted:

 new J(new I(new H(new G(new F(new E(new D(new C(new B(new A())))))))));

And imagine the non-DI version that J create H, H creates G, etc:

new J();

Then the constructor for A changes to need a text file location. This has to be provided by the application developer not the library author as hard coding it in the library presents all kinds of portability issues.

So the only way to do this is alter J to have a constructor argument:

new J('file.txt');

which then passes it to I that passes it to H that passes it down to G etc, etc.

The problem again is the lack of atomicity in this discussion. Rather than being able to discuss the problem at hand it keeps getting pulled back to “What if this happens, what if this happens”. Let’s talk about the pros/cons of DI before discussing further concepts such as containers otherwise we are clearly crossing concerns and conflating different (albeit related) problems.

There are two different discussions happening here. The initial topic was clearly centred around what constitutes well designed software in relation to DI and the reasons for that but we keep straying into anecdotes and unrelated or loosely related topics.

As an analogy: I am trying to discuss the merits of different CPUs. I’ve stated my metric as being “I want the fastest one available for a specific task” so I look at the CPUs analyse them according to my stated criteria and you and tony keep saying “Well what about this motherboard” “Doesn’t this case look pretty?”, “Does it have to be silver??”, “That won’t work with my RAM”, “I have this processor and it works just fine for me”… all I want to do is directly compare processors.

Now, It would be fine to come along and look at them from a different metric and say “I want the cheapest one available” but both Tony and yourself keep failing to identify the metric you are using to compare the different approaches. We’ve briefly skirted around lines of code/development time but as we’ve seen, this isn’t very clear cut as all your examples are mixing in other details (SoC, DI containers, etc).

To reiterate, the metric I am using is essentially the list I posted earlier:

As “Best” in programming tends to mean flexiblity, and the list here is quite good http://c2.com/cgi/wiki?GoodArchitecture :

Robust - lacking bugs and tolerant of external faults
Maintainable - easy to maintain and extend
Useful - utility, beyond the immediate need (due to flexibility and extensibility)
Scalable - ability to grow in capacity, not in features
Common Vision - direction, strategy
Agile - simple and “elegant” enough to refactor easily; flexible
Extensible - ability to grow in features or in depth
Responsive - performance now and after adding features or expanding scale

Edit: And if you do identify another metric. We then need to ask the question: at what point does that metric become important?

You have just confirmed what I have been claiming all along that there is no single document called “best practices” which is universally accepted by all programmers. Instead there is a vast collection of documents written either by individuals, groups or organisations which do nothing more than identify what “best practices” means to them. So if I, or any other programmer for that matter, wishes to find out what is considered to be the current set of best practices, where do I look? If I search google for “best practices” I get 142 million hits. If I search for “best practices software” I get 132 million hits. It would be physically impossible to read through all those documents and obtain a common consensus for the simple reason there is no common consensus. There is no “best for everybody”, just a vast collection of different ideas of what is considered best by different groups. So when you tell me that I am not following “best practices” I am justified in asking the question “which of the 132 million variations of best practices am I not following?” As far as I am concerned I AM following best practices, but not the same practices as you. There is no law that says that I, or every other programmer, has to do everything the same as you, so don’t tell me that I am wrong for not doing so.

Besides, can you point me to any best practices document from any reputable organisation or group which agrees with your idea that I SHOULD use DI, or any other design pattern for that matter, at every possible opportunity, even when the circumstances for which the pattern was designed do not actually exist?

Sometimes a principle or idea gets labelled “bad” because someone tried to implement that idea but failed. But was the failure due to their faulty implementation rather than a fault with the idea? For example, I have been told on numerous occasions that my idea of having a separate class for each database table is just plain wrong. “It’s just not done that way” is the common argument. I even heard from one programmer that he tried the idea but couldn’t get it to work, so he assumed that the idea was unworkable. The simple fact is that I tried that idea in 2002 and got it to work and have been using it very successfully ever since. I proved that the idea works simply by producing an implementation that did not fail.

Any modular or layered system will have dependencies, and where there are dependencies there will automatically be coupling. It is impossible to have a completely de-coupled application as it simply would not work. Unfortunately too many programmers treat any form of coupling as tight coupling when in fact it is actually loose coupling. Loose coupling has fewer problems than tight coupling.

As for “broken encapsulation”, this is caused by the inappropriate use of DI.

I suggest you look at post #1, #10, #12, #26.

I have explained more than once the circumstances where DI is a good solution, which therefore means that where those circumstances do not exist then DI is not a good solution.

This is the point I have been trying to make. I use DI where it is useful, but I don’t use it where it is not useful, but there are some posters in this discussion who seem to think that I should use DI at every possible opportunity, whether it provides any benefits or not, simply because they think it is the right thing to do.

I can’t help it. I have to answer.

Tony, are you seriously just searching for “best practices”? Or did you add “PHP” to the search, because if you do, you’ll end up with a good list of sites with best practices. Below are some of the results I got filtering for results only from 2013 onward, because yes, best practices change over time, as the technology changes.

PHP Best Practices
Best Practices for Modern PHP Development - also mentions dependency injection
Best practices that you must follow
Even Sitepoint’s article is in the list - PHP Tips, Resources and Best Practices for 2015
With some interesting references, like…
PHP the Right Way - even with a section about Dependency Injection (this page is also result number 1, if you search only for “php best practices”.

You can stay blind to the current PHP world Tony. It won’t mind. It will happily leave you in its dust.

Scott

1 Like

None of those millions of sites say that creating a 9000 line class that mixes 10+ responsibilities is a good idea. The fact that dozens or more professional and/or academic well respected programmers all independently come to the same conclusion actually gives it more weight.

So no then. None of these posts do what I asked. I am still waiting for an example of when DI is inferior and for you to explain the metric you are using to measure the difference. Until you do this, I’m going to assume you can’t, which makes your opinion void.

It does not matter how many pages you select from the web labelled “best practices”, they are simply the personal preferences of the person or group who wrote the document based on their experiences. You should see from some of the comments that plenty of developers disagree with what has been written, so it is totally wrong for anyone to say that just because I do not follow a their chosen practices that I must therefore be following bad practices which automatically makes my code bad.

I repeat, there is no single document which is universally accepted as “best practice”, just a huge collection of possible candidates, so I am free to choose whichever standards suit me the best.

The fact that I do not employ the same design patterns as you, or implement them in the same way that you do, is totally irrelevant. Each pattern is a possible solution to a set of circumstances, but if those circumstances don’t exist in my code then I do not see the point of implementing that pattern. Dependency Injection only has benefits when a dependency can be supplied from a number of alternatives, so when I do have a number of alternatives I do use DI, but when don’t have a number of alternatives I don’t use DI. It’s as simple as that.

This is 100% wrong and just proves yet again that you are purposefully missing the point as you have been backed into a corner. Best practices exist and they exist for a reason. They are “Best practices” not the 10 commandments. And while it’s fair to say there is some disagreement about some of the nuances, overall people agree that things like:

  • flexibiltiy
  • maintainability
  • separation of concerns
  • reusability
  • Ease of testing

are good things and

  • Tight coupling
  • Action at a distance
  • Broken encapsulation
  • Global state

Are bad.

You’re trying to turn this into a false dichotomy and imply controversy where there simply is none. It’s like I said in the last thread. You’re sitting on the “vaccines cause autism” side of the debate when all the evidence both independent and from professionals proves they do not. Please either back up your points or stop posting. The burden of proof is on you and you seem incapable of backing up the claims you make.