我在这里看到一个主要问题:
// This class requires some predefined globals
这可能会让您感到惊讶,但我认为您真正想要做的是,如果是这种情况,您不会在定义类时检查它,而是在实例化它时检查。
当一个类被实例化时,它的构造函数被调用。这对我来说似乎是一个完美的地方来检查:
class Product
{
public function __construct() {
// This class requires some predefined globals
$this->needGlobal('gLogger', 'db', 'glob');
}
private function needGlobal() {
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
}
}
...
}
当您实例化 aProduct
时,它会自动检查是否满足先决条件:
$blueShoes = new Product();
如果不满足前提条件,这将不起作用,但如果它会起作用。
但这只是部分解决了您的问题。您的代码的真正问题是Product
需要全局变量才能工作。
而是让产品只使用它需要使用的东西:
class Product
{
private $gLogger;
private $db;
private $glob;
public function __construct(LoggerInterface $gLogger, DbInterface $db, GlobInterface $glob) {
$this->gLogger = $gLogger;
$this->db = $db;
$this->glob = $glob;
}
...
}
用法:
$redShoes = new Product($gLogger, $db, $glob);
然后你就不需要再关心内部的Product
任何全局了。
您评论说您希望逐步改进代码。你可以这样做,方法如下。正如上面所写的第二个变体是要走的路,但目前遗留代码与它不兼容。无论如何,如果Product
该类是新代码,您应该使用依赖注入来编写它。这对于将旧代码与新代码分开很重要。您不希望旧代码被新代码吞噬。那将使新代码遗留代码,因此您将无法逐步改进。您只需添加新的遗留代码。
所以使用依赖注入来定义类。对于您的遗留需求,编写第二个类来屏蔽这个:
class ProductLegacy extends Product
{
public function __construct() {
// This class requires some predefined globals
list($gLogger, $db, $glob) = $this->needGlobal('gLogger', 'db', 'glob');
parent::__construct($gLogger, $db, $glob);
}
private function needGlobal() {
$variables = array();
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
$variables[] = $GLOBALS[$global];
}
return $variables;
}
}
如您所见,这个小存根将全球的做事方式与新的方式结合在一起。您可以Product
在新代码中使用该类,如果您需要与旧代码交互,则可以使用ProductLegacy
与全局变量一起使用的类进行类实例化。
您还可以创建一个辅助函数来执行此操作,以便您可以将它用于不同的类。取决于你的需要。只需找到一个可以在旧代码和新代码之间划清界限的边界。