Наконец-то я приступил к изучению черт в PHP. Первое, что я решил попробовать — это внедрение битов конфигурации в классы. Если я использую DIC, у меня может быть такой код в любом классе, которому нужен объект конфигурации:
protected function SetConfig($config) {
$this->config = $config;
}
protected $config;
Это кажется естественным соответствием чертам, чтобы избежать повсеместного использования этого стандартного кода, поэтому я мог бы создать это:
trait Config {
protected function SetConfig($config) {
$this->config = $config;
}
protected $config;
}
а затем использовать его так:
class Foo {
use Config;
public function __construct() {
//can now use $this->config
}
}
Замечательно. Теперь предположим, что я хочу создать вторую черту, скажем, для ведения журнала:
trait Logger {
protected function SetLogger($logger) {
$this->logger = $logger;
}
protected $logger;
}
Который я могу использовать так:
class Foo {
use Logger;
public function __construct() {
//can now use $this->logger
}
}
Тоже отлично. Теперь проблема возникает, если эти две черты хотят использовать друг друга. Кажется вполне разумным, что в классе логгера должен быть введен объект конфигурации, что означает следующее:
trait Logger {
use Config;
protected function SetLogger($logger) {
$this->logger = $logger;
}
protected $logger;
}
Но тогда все сломается, когда другой класс использует обе эти черты:
class Foo {
use Config, Logger;
public function __construct() {
//want to use $this->config and $this->logger
}
}
Это, конечно, не работает, потому что биты конфигурации эффективно дублируются в Foo.
Я мог бы просто опустить use Config;
часть от черты Logger, зная, что это будет там в конце. Но это кажется мне странным, поскольку создает некую внешнюю зависимость. Что если я захочу использовать Logger где-нибудь, у которого еще нет черты конфигурации? Это решение также означает, что мне нужно, чтобы моя IDE (PhpStorm 8) предупреждала меня о неизвестных методах и не предлагала автозаполнение. Я понимаю, что могу исправить эти проблемы по очереди, используя @method, но это, так сказать, просто нанесение помады на свинью.
Я мог бы также использовать псевдоним битов конфигурации в Logger, но это также проблематично.
Все это имеет какой-то запах, но я еще не выяснил, потому что это новый шаблон для меня или действительно вонючий шаблон. В любом случае, я не уверен, что лучший способ заставить этот подход работать на самом деле.
Любой совет, как лучше всего решить эту проблему в чертах? Или лучше избегать черт быстрого доступа к DIC?
Метод, который я нашел полезным, использует как методы получения, так и установки. Это тогда позволяет вам требовать, чтобы определенный геттер существовал, не вступая в конфликт с другими чертами.
trait Config {
protected function SetConfig($config) {
$this->config = $config;
}
protected function GetConfig() {
return $this->config;
}
protected $config;
}
trait Logger {
abstract protected function GetConfig();
protected function SetLogger($logger) {
$this->logger = $logger;
}
protected $logger;
}
class Baz {
use Config, Logger;
// ...
}
В Baz черта Config предоставляет требуемый абстрактный метод, а Baz составляется без ошибок. Если вы по ошибке используете только Logger, вы получите фатальную ошибку: класс Baz содержит 1 абстрактный метод и поэтому должен быть объявлен как абстрактный или реализовать оставшиеся методы (Baz :: GetConfig)
Других решений пока нет …