PHP dependency injection and Loose Coupling

I reflect on a few different approaches here and will be very grateful for the contribution! I consider two options below. There are 2 things that happen there on which I have questions.

  • Is it preferable to introduce dependencies in the constructor of the main class "container" or instead create new instances inside the class of the container?

  • In the second example, class dependencies are introduced through the constructor and then supported internally through the class property. Then, when the methods (route (), render ()) are called, the dependencies are called from within. I started with this approach, but now I prefer something more than the first example. I think the first example is preferable, but are there any advantages to using the DI approach in the second example?

In fact, there is no need to store anything in the class as a property. I can probably change everything to use this technique without any problems, and I think I like it. That way, I can also move all the work from the constructors and just access all through the method later. Am I on the right track here?

class App { private $config; private $router; private $renderer; public function __construct(IConfig $config, IRouter $router, IRenderer $renderer) { $this->config = $config; $this->router = $router; $this->renderer = $renderer; $this->run(); } public function run() { $data = $this->router->route(new Request, $config->routes); $this->renderer->render($data); } } class App { private $config; private $router; private $renderer; public function __construct() { $this->config = new Config; $this->run(); } public function run() { $this->router = new Router(new Request, $config->routes); $this->router->route(); $this->renderer = new Renderer($this->router->getData()); $this->renderer->render(); } } 
+6
source share
2 answers

It is better to introduce dependencies in the constructor.

Creating instances within the constructor creates a tight connection between the two classes. With a constructor with a clear signature type

  public function __construct(IConfig $config, IRouter $router, IRenderer $renderer) 

I can immediately say that this component should do the job.

For a designer like

 public function __construct(); 

It is not possible to determine which component should function. It creates a strong connection with the specific implementations of each of your routers, your request, and your renderer, not one of which appears until you dig into the guts of your class.

In general, the first approach is well documented, extensible and verifiable. the second approach is opaque, tightly coupled, and not easy to verify.

+7
source

While OrangePill makes a good point, it seemed to me that I turned on too. I also tend to define my constructors with an explicit constructor, but I do not expect the required objects to be passed when the instance is created.
Sometimes you create an instance that retrieves data from a database or some Http request. In your case, the first example expects the transfer of three dependencies, but who says that you will always need all three of them?

Enter lazy loading. The code example below is quite long, but it (IMO) is worth a look. If I use the service, I do not want to download all the dependencies unless I am sure that I will use them. Therefore, I defined a constructor to create an instance in one of the following ways:

 $foo = new MyService($configObj); $bar = new MyService($configObj, null, $dbObj);//don't load curl (yet) $baz = new MyService($configObj, $curlObj);//don't load db (yet) 

If I wanted to run some kind of test, I can still add dependencies when building my instance, or I can rely on the test-config object, or I could use the setDb and setCurl too:

 $foo->setCurl($testCurl); 

Following the first method of building an instance, I can safely say that if I just call the getViaCurl method, the Db class will never be loaded.
The getViaDb method is more complex (like the getDb method). I do not recommend you work with similar methods, but it just shows you how flexible this approach can be. I can pass an array of parameters to the getViaDb method, which can contain a custom connection. I can also pass a boolean value that will control what I'm doing with this connection (use it only for this call or assign a connection to the MyService instance.

I hope this is not too incomprehensible, but I'm pretty tired, so I'm not too good at this ATM.
Here's the code, anyway ... it should be very clear.

 class MyService { private $curl = null; private $db = null; private $conf = null; public function __construct(Config $configObj, Curl $curlObj = null, Db $dbObj = null) { $this->conf = $configObj;//you'll see why I do need this in a minute $this->curl = $curlObj;//might be null $this->db = $dbObj; } public function getViaCurl(Something $useful) { $curl = $this->getCurl();//<-- this is where the magic happens return $curl->request($useful); } public function getViaDb(array $params) { if (isset($params['custom'])) { $db = $this->getDb($params['custom'], $params['switch']); } else {//default $db = $this->getDb(); } return $db->query($params['request']); } public function getCurl() {//return current Curl, or load default if none set if ($this->curl === null) {//fallback to default from $this->conf $this->curl = new Curl($this->conf->getSection('CurlConf')); } return $this->curl; } public function setCurl(Curl $curlObj) {//inject after instance is created here if ($this->curl instanceof Curl) {//close current connection $this->curl->close(); } $this->curl = $curlObj; } public function setDb(Db $dbObj) { if ($this->db instanceof Db) {//commit & close $this->db->commit(); $this->db->close(); } $this->db = $dbObj; } //more elaborate, even: public function getDb(Db $custom = null, $switch = false) { if ($custom && !!$swith === true) { $this->setDb($custom); return $this->db; } if ($custom) {//use custom Db, only this one time return $custom; } if ($this->db === null) { $this->db = new Db($this->conf->getSection('Db')); } return $this->db; } } 
+2
source

Source: https://habr.com/ru/post/946233/


All Articles