【发布时间】:2011-07-26 17:32:51
【问题描述】:
我正在构建一个小项目,试图尽可能多地自学基础知识,这对我来说意味着不使用预制框架(如 Jeff once put it,“不要重新发明轮子,除非您打算进一步了解车轮”[强调我的])并遵循测试驱动开发的原则。
在我的探索中,我最近遇到了依赖注入的概念,这对 TDD 来说似乎很重要。我的问题是我无法完全理解它。到目前为止,我的理解是,它或多或少相当于“让调用者传递类/方法它可能需要的任何其他类,而不是让它们自己创建它们。”
我尝试使用 DI 解决两个示例问题。我在这些重构方面走在正确的轨道上吗?
数据库连接
我打算只使用一个单例来处理数据库,因为我目前不希望使用多个数据库。最初,我的模型看起来像这样:
class Post {
private $id;
private $body;
public static function getPostById($id) {
$db = Database::getDB();
$db->query("SELECT...");
//etc.
return new Post($id, $body);
}
public function edit($newBody) {
$db = Database::getDB();
$db->query("UPDATE...");
//etc.
}
}
使用 DI,我认为它看起来更像这样:
class Post {
private $db; // new member
private $id;
private $body;
public static function getPostById($id, $db) { // new parameter
$db->query("SELECT..."); // uses parameter
//etc.
return new Post($db, $id, $body);
}
public function edit($id, $newBody) {
$this->db->query("UPDATE..."); // uses member
//etc.
}
}
我仍然可以使用单例,并在应用程序设置中指定凭据,但我只需要从控制器传递它(控制器无论如何都是不可单元测试的):
Post::getPostById(123, Database::getDB);
模型调用模型
以具有查看次数的帖子为例。由于确定视图是否为新视图的逻辑并不特定于 Post 对象,因此它只是其自身对象的静态方法。 Post 对象会调用它:
class Post {
//...
public function addView() {
if (PageView::registerView("post", $this->id) {
$db = Database::getDB();
$db->query("UPDATE..");
$this->viewCount++;
}
}
使用 DI,我认为它看起来更像这样:
class Post {
private $db;
//...
public function addView($viewRegistry) {
if ($viewRegistry->registerView("post", $this->id, $this->db) {
$this->db->query("UPDATE..");
$this->viewCount++;
}
}
这会将来自控制器的调用更改为:
$post->addView(new PageView());
这意味着实例化一个只有静态方法的类的新实例,这对我来说很糟糕(我认为在某些语言中是不可能的,但在这里可行,因为 PHP 不允许类本身是静态的)。
在这种情况下,我们只深入一层,因此让控制器实例化一切似乎是可行的(尽管 PageView 类通过 Post 的成员变量间接获取其 DB 连接),但它似乎可以得到如果您必须调用需要类的方法,而该类需要需要类的类,则该方法很笨拙。我想这可能只是意味着这也是一种代码味道。
我是在正确的轨道上,还是我完全误解了 DI?非常感谢任何批评和建议。
【问题讨论】:
标签: php oop model-view-controller dependency-injection tdd