【发布时间】:2014-10-10 20:12:39
【问题描述】:
我终于开始探索 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 快捷方式的特征?
【问题讨论】:
-
我自己从未将
traits用于DI,但我可以提出的一个建议是,如果您有需要其他特征的特征,您可能会受益于将它们转换为类。 -
一个 logger trait 可能需要配置来知道使用什么日志级别或在哪里写入日志文件。但实际上,问题不仅仅是这个案例——这只是一个例子。更广泛地说,核心功能之间存在依赖关系当然并不少见。在每个类中使用样板代码(属性和设置器)时,这不是问题,但这是特征的问题。在这种情况下,将
use Config添加到 Logger 将不起作用,因为同时使用 Logger 和 Config(上图 Foo)的类将生成致命错误,因为 Config 中的片段重复。 -
这就是我问这个问题的原因。正如我所说,它有一种奇怪的气味,但与此同时,特征似乎是解决 DI 中固有的样板问题的好方法。那么将两者结合在一起的最佳方法是什么?
-
我认为“使用 Trait”应该更像 require_once。由于特征无论如何都被压平了,因此忽略重复性并丢弃其中一个并没有什么害处。这种情况下的致命错误对我来说似乎太严重了。
标签: php design-patterns dependency-injection traits