PHP依赖注入和依赖容器与事件分发器

3
阅读了这篇关于依赖注入的优秀文章之后:http://miller.limethinking.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/,我意识到我的小型框架在很大程度上已经使用了依赖注入容器。
我有一个App类来管理所有服务的创建,如Database、Config、Session,并且能够实例化不同的类。
但是我意识到其中一部分会引起问题:我将事件调度器传递给框架中许多基类,但是每个基类都像这样使用调度器:
$this->dispatcher->fire(new FB_Event('FB.Database.Model.beforeUpdate', $this, array('pk' => $pk, 'row' => $row)));

如您所见,我在这里使用了一个new,因此无法完全对我的类进行单元测试。我不想将我的应用程序容器传递给所有应用程序,因为它包含数据库、会话和其他类不需要的大量服务实例,调试起来很麻烦。

既然不能通过依赖注入传递FB_Event,那么最好的解决方案是什么呢?因为同一个函数通常会触发2个事件:

public function delete($pk) {
    $e = $this->dispatcher->fire(new FB_Event('FB.Database.Model.beforeDelete', $this, array('pk' => $pk)));
    $pk = $e->params['pk'];
    if ($e->preventDefault()) {
        return false;
    }

    $this->requirePK();
    list ( $where, $params ) = $this->pkParts(null, $pk);
    $sql = "DELETE FROM {$this->name} WHERE $where";
    $stm = $this->db->execute($sql, $params);

    $this->dispatcher->fire(new FB_Event('FB.Database.Model.afterDelete', $this, array('pk' => $pk)));
    return!(bool) $stm->errorCode();
}

我的解决方案之一是向调度程序添加一个名为“fireEvent”的新函数,它将负责创建新的FB_Event并消除到处都存在的耦合。但会在调度程序和事件之间增加耦合,这种情况比较不好。

public function fireEvent($name, $target, $params = array()) {
    return $this->fire(new FB_Event($name, $target, $params = array()));
}

您是否会在容器中创建某种EventFactory并将其传递给Dispatcher?这样做是否过度设计,因为仅涉及小型对象如Event?

感谢您的建议。

2个回答

1
什么是最好的解决方法,因为我无法通过依赖注入传递FB_Event,因为同一函数通常会触发2个事件?
您可以通过在FB_Event类中使用setter来解决此问题。例如,将FB_Event(DI)的实例传递给相关类,然后将其用作:
public function delete($pk) {

    $this->fb_event->setName('FB.Database.Model.beforeDelete');     
    $this->fb_event->setTarget($this);
    $this->fb_event->setParams(array('pk' => $pk));

    $e = $this->dispatcher->fire($this->fb_event);
    $pk = $e->params['pk'];
    if ($e->preventDefault()) {
        return false;
    }

    $this->requirePK();
    list ( $where, $params ) = $this->pkParts(null, $pk);
    $sql = "DELETE FROM {$this->name} WHERE $where";
    $stm = $this->db->execute($sql, $params);

    $this->fb_event->reset();
    $this->fb_event->setName('FB.Database.Model.afterDelete'); 
    $this->fb_event->setTarget($this);
    $this->fb_event->setParams(array('pk' => $pk));    

    $this->dispatcher->fire($this->fb_event);
    return!(bool) $stm->errorCode();
}

这种方法可能有效,但我更喜欢拥有不同的实例,以防万一,因为调度方法返回具有修改参数的事件对象。 - inkubux

1

我认为你提出的解决方案是最好的答案。你说得对,在代码中实例化FB_Event应该避免,因为这会使测试变得困难,并且耦合了你的代码。是的,在你的fireEvent方法中,Dispatcher类和Event类之间存在耦合,但是Dispatcher应该知道Events,因为这就是它正在分派的内容。是的,应该避免耦合,但有些类有时确实需要知道其他类的情况;不能总是避免耦合。你的fireEvent方法的好处在于它是一个接缝,因此将来可以用它来打破代码。


我终于意识到Event是一个非常简单的对象,fireEvent方法是将所有基类与FB_Event解耦的最佳方式。 - inkubux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接