📜 ⬆️ ⬇️

PHP and Temporal Coupling

About a dozen articles were written about PHP application architecture, but Java and C # developers focus on this problem more. Its essence lies in the rigid dependence of one property on another.

Imagine the following situation:

Use setter
<?php class Page { /** * @var string */ private $content; /** * @param $content */ public function setContent($content) { $this->content = $content; } } class PageBuilder { /** * @var Page */ private $page; /** * @param $page */ public function setPage($page) { $this->page = $page; } /** * @param $content * @return $this */ public function setContent($content) { $this->page->setContent($content); return $this; } /** * @return Page */ public function build() { return $this->page; } } 


 $pageBuilder = new PageBuilder(); $pageBuilder->setPage(new Page()); $pageBuilder->setContent('Test content'); $pageBuilder->build(); 

In this example, you can see that $ pageBuilder-> build () is potentially dangerous and can lead to a fatal error if $ pageBuilder-> setPage (new Page ()) was not previously called. Another common mistake is using the init () or initialization () methods:

Use initializer
 class Page { // Class } class PageBuilder { /** * @var Page */ private $page; /** * Initialization */ public function init() { $this->page = new Page(); } /** * @param $content * @return $this */ public function setContent($content) { $this->page->setContent($content); return $this; } /** * @return Page */ public function build() { return $this->page; } } 


 $pageBuilder = new PageBuilder(); $pageBuilder->init(); $pageBuilder->setContent('Test content'); $pageBuilder->build(); 

If we forget to call the init () method, we are also in trouble. This code is a great example of a poor application architecture. Initialization methods try to behave like constructors that they are not by definition.
')
To avoid Temporal Coupling, you should always use the rules:


Dependency injection through constructor


This solution is optimal and preferred in most cases. We can use Dependency Injection mechanisms from symfony, Laravel, or other modern frameworks.

Injection through the constructor
 class Page { // Class } class PageBuilder { /** * @var Page */ private $page; /** * PageBuilder constructor. * @param Page $page */ public function __construct(Page $page) { $this->page = $page; } // - } $pageBuilder = new PageBuilder(new Page()); $pageBuilder->setContent('Test content'); $pageBuilder->build(); 


Abstract Factory


We modify our code a bit by adding an abstract factory:

Abstract Factory
 <?php class Page { // Class } class PageBuilder { /** * @var Page */ private $page; /** * PageBuilder constructor. * @param Page $page */ public function __construct(Page $page) { $this->page = $page; } /** * @param $content * @return $this */ public function setContent($content) { $this->page->setContent($content); return $this; } /** * @return Page */ public function build() { return $this->page; } } class PageBuilderFactory implements FactoryInterface { /** * @param Page|null $page * @return PageBuilder */ public function create(Page $page = null) { if (null === $page) { $page = new Page(); } return new PageBuilder($page); } } $pageBuilderFactory = new PageBuilderFactory(); $pageBuilder = $pageBuilderFactory->create(); $pageBuilder->setContent('Test content'); $pageBuilder->build(); 


As you can see, an instance of the Page class was created without an explicit call and will be available to our builder.

Conclusion


Temporal Coupling should always be avoided, regardless of the complexity of the application, the impact of code-review or other factors. Also remember that designers should only perform logic related to injections. Otherwise, you risk getting a performance degradation during the creation of class instances.

useful links


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


All Articles