我一直在阅读/观看很多推荐的材料,最近一次是这个 –
MVC for advanced PHP developers.有一件事是Singletons很糟糕,他们在类之间创建依赖关系,而依赖注入很好,因为它允许单元测试和解耦.
在我编写程序之前,这一切都很好.我们以电子商店的产品页面为例.首先我有我的页面:
class Page {
public $html;
public function __construct() {
}
public function createPage() {
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
到目前为止一切都很好,但页面需要一个产品,所以让我们通过一个:
class Page {
public $html;
private $product;
public function __construct(Product $product) {
$this->product = $product;
}
public function createPage() {
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
我已经使用依赖注入来避免使我的页面类依赖于产品.但是如果页面有几个公共变量并且在调试时我想看看那些是什么.没问题,我只是var_dump()页面实例.它为我提供了页面中的所有变量,包括产品对象,因此我也获得了产品中的所有变量.
但是产品不只是包含产品实例化的所有细节的所有变量,它还有一个数据库连接来获取那些产品细节.所以现在我的var_dump()也有数据库对象.现在它开始变得更长,更难以阅读,即使在< pre>标签.
产品也属于一个或多个类别.为了论证,让我们说它属于两个类别.它们在构造函数中加载并存储在包含数组的类变量中.所以现在我不仅拥有产品和数据库连接中的所有变量,还拥有类别类的两个实例.当然,类别信息也必须从数据库加载,因此每个类别实例也有一个数据库私有变量.
所以现在当我在var_dump()我的页面时,我拥有所有页面变量,所有产品变量,数组中类别变量的倍数,以及数据库变量的3个副本(一个来自产品实例,一个来自每个类别)实例).我的输出现在很大,难以阅读.
现在单身人士怎么样?让我们看一下使用单例的页面类.
class Page {
public $html;
public function __construct() {
}
public function createPage() {
$prodId = Url::getProdId();
$productInfo = Product::instance($prodId)->info();
// do something to generate the page
}
public function showPage() {
echo $this->html;
}
}
我也在Product类中使用类似的单例.现在,当我的var_dump()我的Page实例时,我只获得了我想要的变量,那些属于页面的变量,没有别的.
但是当然这会在我的类之间产生依赖关系.在单元测试中,没有办法不调用产品类,使单元测试变得困难.
如何获得依赖注入的所有好处,但仍然可以使用var_dump()轻松调试我的类?如何避免将所有这些实例作为变量存储在我的类中?
最佳答案 我会尝试在这里写几件事.
关于var_dump():
我使用Symfony2作为默认框架,有时,var_dump()是快速调试的最佳选择.但是,它可以输出如此多的信息,你无法阅读所有信息,对吧?比如,转储Symfony的AppKernel.php,或者更接近你的情况,一些具有EntityManager依赖性的服务.恕我直言,var_dump()在调试少量代码时很不错,但是大而复杂的产品使得var_dump()无效.替代方案是使用与IDE集成的“真实”调试器.使用PhpStorm下的xDebug,我不再需要var_dump()了.
关于“为什么?”的有用链接和“如何?”是here.
关于DI容器:
它的忠实粉丝.它很简单,使代码更稳定;它在现代应用中很常见.但我同意你的看法,背后有一个真正的问题:嵌套依赖.这是过度抽象,通过添加有时不必要的层会增加复杂性.
Masking the pain by using a dependency injection container is making
your application more complex.
如果您想从应用程序中删除DIC,并且实际上可以执行此操作,那么您根本不需要DIC.如果您想要替代DIC,那么……单身人士被认为是不可测试的代码和应用程序的巨大状态空间的不良做法.我的服务定位器有no benefits at all.所以看起来有唯一的方法,学习使用DI吧.
关于你的例子:
我立即看到了一件事 – 通过construct()注入.这很酷,但我更喜欢可选的传递依赖关系到需要它的方法,例如通过服务config.yml中的setter.
class Page
{
public $html;
protected $em;
protected $product;
public function __construct(EntityManager $em) {
$this->em = $em;
}
//I suppose it's not from DB, because in this case EM handles this for you
protected function setProduct(Product $product)
{
$this->product = $product;
}
public function createPage()
{
//$this->product can be used here ONLY when you really need it
// do something to generate the page
}
public function showPage()
{
echo $this->html;
}
}
我认为它在执行期间只需要一些对象时提供了所需的灵活性,在给定的时刻,您只能在类中看到所需的属性.
结论
请原谅我的答案.我真的认为你的问题没有直接的答案,任何解决方案都是基于意见的.我只是希望您可能会发现DIC确实是具有有限缺点的最佳解决方案,以及集成调试器而不是倾销整个类(构造函数,服务等等).