php – 你能使用依赖注入并仍然避免许多私有变量吗?

我一直在阅读/观看很多推荐的材料,最近一次是这个 –
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确实是具有有限缺点的最佳解决方案,以及集成调试器而不是倾销整个类(构造函数,服务等等).

点赞